Ver Fonte

Initial commit.

Gilbert Pellegrom há 13 anos atrás
commit
92b792e8f2
100 ficheiros alterados com 10112 adições e 0 exclusões
  1. 9 0
      .htaccess
  2. 1 0
      README
  3. 17 0
      config.php
  4. 9 0
      content/404.txt
  5. 77 0
      content/index.txt
  6. 6 0
      content/sub/index.txt
  7. 6 0
      content/sub/page.txt
  8. 19 0
      index.php
  9. 87 0
      lib/cache/b0/36/0ab98142b2e0bbb248aad053058f.php
  10. 1732 0
      lib/markdown.php
  11. 114 0
      lib/pico.php
  12. 9 0
      lib/twig/AUTHORS
  13. 474 0
      lib/twig/CHANGELOG
  14. 31 0
      lib/twig/LICENSE
  15. 15 0
      lib/twig/README.markdown
  16. 42 0
      lib/twig/bin/create_pear_package.php
  17. 27 0
      lib/twig/composer.json
  18. 489 0
      lib/twig/doc/advanced.rst
  19. 479 0
      lib/twig/doc/api.rst
  20. 101 0
      lib/twig/doc/coding_standards.rst
  21. 328 0
      lib/twig/doc/extensions.rst
  22. 11 0
      lib/twig/doc/filters/capitalize.rst
  23. 22 0
      lib/twig/doc/filters/convert_encoding.rst
  24. 65 0
      lib/twig/doc/filters/date.rst
  25. 28 0
      lib/twig/doc/filters/default.rst
  26. 30 0
      lib/twig/doc/filters/escape.rst
  27. 16 0
      lib/twig/doc/filters/format.rst
  28. 29 0
      lib/twig/doc/filters/index.rst
  29. 18 0
      lib/twig/doc/filters/join.rst
  30. 14 0
      lib/twig/doc/filters/json_encode.rst
  31. 11 0
      lib/twig/doc/filters/keys.rst
  32. 12 0
      lib/twig/doc/filters/length.rst
  33. 10 0
      lib/twig/doc/filters/lower.rst
  34. 41 0
      lib/twig/doc/filters/merge.rst
  35. 22 0
      lib/twig/doc/filters/nl2br.rst
  36. 38 0
      lib/twig/doc/filters/number_format.rst
  37. 12 0
      lib/twig/doc/filters/raw.rst
  38. 14 0
      lib/twig/doc/filters/replace.rst
  39. 23 0
      lib/twig/doc/filters/reverse.rst
  40. 57 0
      lib/twig/doc/filters/slice.rst
  41. 17 0
      lib/twig/doc/filters/sort.rst
  42. 15 0
      lib/twig/doc/filters/striptags.rst
  43. 11 0
      lib/twig/doc/filters/title.rst
  44. 24 0
      lib/twig/doc/filters/trim.rst
  45. 10 0
      lib/twig/doc/filters/upper.rst
  46. 14 0
      lib/twig/doc/filters/url_encode.rst
  47. 18 0
      lib/twig/doc/functions/attribute.rst
  48. 15 0
      lib/twig/doc/functions/block.rst
  49. 9 0
      lib/twig/doc/functions/constant.rst
  50. 20 0
      lib/twig/doc/functions/cycle.rst
  51. 46 0
      lib/twig/doc/functions/date.rst
  52. 58 0
      lib/twig/doc/functions/dump.rst
  53. 15 0
      lib/twig/doc/functions/index.rst
  54. 20 0
      lib/twig/doc/functions/parent.rst
  55. 24 0
      lib/twig/doc/functions/random.rst
  56. 38 0
      lib/twig/doc/functions/range.rst
  57. 184 0
      lib/twig/doc/hacking.rst
  58. 18 0
      lib/twig/doc/index.rst
  59. 153 0
      lib/twig/doc/intro.rst
  60. 301 0
      lib/twig/doc/recipes.rst
  61. 43 0
      lib/twig/doc/tags/autoescape.rst
  62. 11 0
      lib/twig/doc/tags/block.rst
  63. 12 0
      lib/twig/doc/tags/do.rst
  64. 187 0
      lib/twig/doc/tags/extends.rst
  65. 21 0
      lib/twig/doc/tags/filter.rst
  66. 17 0
      lib/twig/doc/tags/flush.rst
  67. 149 0
      lib/twig/doc/tags/for.rst
  68. 8 0
      lib/twig/doc/tags/from.rst
  69. 43 0
      lib/twig/doc/tags/if.rst
  70. 79 0
      lib/twig/doc/tags/import.rst
  71. 86 0
      lib/twig/doc/tags/include.rst
  72. 22 0
      lib/twig/doc/tags/index.rst
  73. 91 0
      lib/twig/doc/tags/macro.rst
  74. 16 0
      lib/twig/doc/tags/raw.rst
  75. 32 0
      lib/twig/doc/tags/set.rst
  76. 37 0
      lib/twig/doc/tags/spaceless.rst
  77. 123 0
      lib/twig/doc/tags/use.rst
  78. 719 0
      lib/twig/doc/templates.rst
  79. 11 0
      lib/twig/doc/tests/constant.rst
  80. 30 0
      lib/twig/doc/tests/defined.rst
  81. 10 0
      lib/twig/doc/tests/divisibleby.rst
  82. 11 0
      lib/twig/doc/tests/empty.rst
  83. 10 0
      lib/twig/doc/tests/even.rst
  84. 14 0
      lib/twig/doc/tests/index.rst
  85. 12 0
      lib/twig/doc/tests/null.rst
  86. 10 0
      lib/twig/doc/tests/odd.rst
  87. 11 0
      lib/twig/doc/tests/sameas.rst
  88. 30 0
      lib/twig/ext/twig/.gitignore
  89. 22 0
      lib/twig/ext/twig/LICENSE
  90. 8 0
      lib/twig/ext/twig/config.m4
  91. 8 0
      lib/twig/ext/twig/config.w32
  92. 49 0
      lib/twig/ext/twig/php_twig.h
  93. 1025 0
      lib/twig/ext/twig/twig.c
  94. 46 0
      lib/twig/lib/Twig/Autoloader.php
  95. 242 0
      lib/twig/lib/Twig/Compiler.php
  96. 35 0
      lib/twig/lib/Twig/CompilerInterface.php
  97. 1060 0
      lib/twig/lib/Twig/Environment.php
  98. 176 0
      lib/twig/lib/Twig/Error.php
  99. 20 0
      lib/twig/lib/Twig/Error/Loader.php
  100. 21 0
      lib/twig/lib/Twig/Error/Runtime.php

+ 9 - 0
.htaccess

@@ -0,0 +1,9 @@
+<IfModule mod_rewrite.c>
+RewriteEngine On
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d 
+RewriteRule . index.php [L]
+</IfModule>
+
+# Prevent file browsing
+Options -Indexes

+ 1 - 0
README

@@ -0,0 +1 @@
+Pico is a stupidly simple, blazing fast, flat file CMS. See http://pico.dev7studios.com for more info.

+ 17 - 0
config.php

@@ -0,0 +1,17 @@
+<?php 
+
+/*
+Override any of the default settings below:
+
+$config['site_title'] = 'Pico';				// Site title
+$config['base_url'] = ''; 				// Override base URL (e.g. http://example.com)
+$config['theme'] = 'default'; 			// Set the theme (defaults to "default")
+$config['enable_cache'] = false; 		// Enable caching
+
+To add a custom config setting:
+
+$config['custom_setting'] = 'Hello'; 	// Can be accessed by {{ config.custom_setting }} in a theme
+
+*/
+
+?>

+ 9 - 0
content/404.txt

@@ -0,0 +1,9 @@
+/*
+Title: Error 404
+Robots: noindex,nofollow
+*/
+
+Error 404
+=========
+
+Woops. Looks like this page doesn't exist.

+ 77 - 0
content/index.txt

@@ -0,0 +1,77 @@
+/*
+Title: Welcome
+Description: This description will go in the meta description tag
+*/
+
+Welcome to Pico
+===============
+
+Congratulations you have successfully installed [Pico](http://pico.dev7studios.com). Pico is a stupidly simple, blazing fast, flat file CMS.
+
+Creating Content
+----------------
+
+Pico is a flat file CMS, this means there is no administration backend and database to deal with. You simply create `.txt` files in the "content"
+folder and that becomes a page. For example this file is called `index.txt` and is shown as the main landing page. 
+
+If you created folder within the content folder (e.g. `content/sub`) and put an `index.txt` inside it, you can access that folder at the URL 
+`http://yousite.com/sub`. If you want another page within the sub folder, simply create a text file with the corresponding name (e.g. `content/sub/page.txt`)
+and will be able to access it from the URL `http://yousite.com/sub/page`. Below we've shown some examples of content locations and their corresponing URL's:
+
+<table>
+	<thead>
+		<tr><th>Physical Location</th><th>URL</th></tr>
+	</thead>
+	<tbody>
+		<tr><td>content/index.txt</td><td>/</td></tr>
+		<tr><td>content/sub.txt</td><td>/sub</td></tr>
+		<tr><td>content/sub/index.txt</td><td>/sub (same as above)</td></tr>
+		<tr><td>content/sub/page.txt</td><td>/sub/page</td></tr>
+		<tr><td>content/a/very/long/url.txt</td><td>/a/very/long/url</td></tr>
+	</tbody>
+</table>
+
+If a file cannot be found, the file `content/404.txt` will be shown.
+
+Text File Markup
+----------------
+
+Text files are marked up using [Markdown](http://daringfireball.net/projects/markdown/syntax). They can also contain regular HTML.
+
+At the top of text files you can place a block comment and specify certain attributes of the page. For example:
+
+	/ *
+	Title: Welcome
+	Description: This description will go in the meta description tag
+	Robots: noindex,nofollow
+	*/
+
+These values will be contained in the `{{ meta }}` variable in themes (see below).
+
+There are also certain variables that you can use in your text files:
+
+* &#37;base_url&#37; - The URL to your Pico site
+
+Themes
+------
+
+You can create themes for your Pico installation and in the "themes" folder. Check out the default theme for an example of a theme. Pico uses
+[Twig](http://twig.sensiolabs.org/documentation) for it's templating engine. You can select your theme by setting the `$config['theme']` variable
+in config.php to your theme folder.
+
+All themes must include an `index.html` file to define the HTML structure of the theme. Below are the Twig variables that are available to use in your theme:
+
+* `{{ config }}` - Conatins the values you set in config.php (e.g. `{{ config.theme }}` = "default")
+* `{{ base_dir }}` - The path to your Pico root directory
+* `{{ base_url }}` - The URL to your Pico site
+* `{{ theme_dir }}` - The path to the Pico active theme direcotry
+* `{{ theme_url }}` - The URL to the Pico active theme direcotry
+* `{{ site_title }}` - Shortcut to the site title (defined in config.php)
+* `{{ meta }}` - Contains the meta values from the current page (e.g. `{{ meta.title }}`, `{{ meta.description }}`, `{{ meta.robots }}`)
+* `{{ content }}` - The content of the current page (after it has been processed through Markdown)
+
+Config
+------
+
+You can override the default Pico settings (and add your own custom settings) by editing config.php in the root Pico directory. The config.php file
+list all of the settings and their defaults. To override a setting simply uncomment it in config.php and set your custom value.

+ 6 - 0
content/sub/index.txt

@@ -0,0 +1,6 @@
+/*
+Title: Sub Page Index
+*/
+
+This is a Sub Page Index
+========================

+ 6 - 0
content/sub/page.txt

@@ -0,0 +1,6 @@
+/*
+Title: Sub Page
+*/
+
+This is a Sub Page
+==================

+ 19 - 0
index.php

@@ -0,0 +1,19 @@
+<?php
+/*
+ * Pico v0.1
+ */
+
+// Defines
+define('ROOT_DIR', realpath(dirname(__FILE__)) .'/');
+define('CONTENT_DIR', ROOT_DIR .'content/');
+define('LIB_DIR', ROOT_DIR .'lib/');
+define('THEMES_DIR', ROOT_DIR .'themes/');
+define('CACHE_DIR', LIB_DIR .'cache/');
+
+require('config.php');
+require(LIB_DIR .'markdown.php');
+require(LIB_DIR .'twig/lib/Twig/Autoloader.php');
+require(LIB_DIR .'pico.php');
+$pico = new Pico();
+
+?>

+ 87 - 0
lib/cache/b0/36/0ab98142b2e0bbb248aad053058f.php

@@ -0,0 +1,87 @@
+<?php
+
+/* index.html */
+class __TwigTemplate_b0360ab98142b2e0bbb248aad053058f extends Twig_Template
+{
+    protected function doDisplay(array $context, array $blocks = array())
+    {
+        // line 1
+        echo "<!DOCTYPE html>
+<html lang=\"en\" class=\"no-js\">
+<head>
+    <meta charset=\"utf-8\" />
+    
+    <title>";
+        // line 6
+        if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; }
+        if ($this->getAttribute($_meta_, "title")) {
+            if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; }
+            echo $this->getAttribute($_meta_, "title");
+            echo " | ";
+        }
+        if (isset($context["site_title"])) { $_site_title_ = $context["site_title"]; } else { $_site_title_ = null; }
+        echo $_site_title_;
+        echo "</title>
+    ";
+        // line 7
+        if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; }
+        if ($this->getAttribute($_meta_, "description")) {
+            echo "<meta name=\"description\" content=\"";
+            if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; }
+            echo $this->getAttribute($_meta_, "description");
+            echo "\">";
+        }
+        // line 8
+        echo "    ";
+        if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; }
+        if ($this->getAttribute($_meta_, "robots")) {
+            echo "<meta name=\"robots\" content=\"";
+            if (isset($context["meta"])) { $_meta_ = $context["meta"]; } else { $_meta_ = null; }
+            echo $this->getAttribute($_meta_, "robots");
+            echo "\">";
+        }
+        // line 9
+        echo "    
+    <link rel=\"stylesheet\" href=\"";
+        // line 10
+        if (isset($context["theme_url"])) { $_theme_url_ = $context["theme_url"]; } else { $_theme_url_ = null; }
+        echo $_theme_url_;
+        echo "/style.css\" type=\"text/css\" media=\"screen\" />
+
+    <!--[if IE]>
+\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">
+\t<script type=\"text/javascript\" src=\"https://html5shiv.googlecode.com/svn/trunk/html5.js\"></script>
+    <![endif]-->
+    <script src=\"";
+        // line 16
+        if (isset($context["theme_url"])) { $_theme_url_ = $context["theme_url"]; } else { $_theme_url_ = null; }
+        echo $_theme_url_;
+        echo "/scripts/modernizr-1.7.min.js\"></script>
+</head>
+<body>
+
+    ";
+        // line 20
+        if (isset($context["content"])) { $_content_ = $context["content"]; } else { $_content_ = null; }
+        echo $_content_;
+        echo "
+
+</body>
+</html>";
+    }
+
+    public function getTemplateName()
+    {
+        return "index.html";
+    }
+
+    public function isTraitable()
+    {
+        return false;
+    }
+
+    public function getDebugInfo()
+    {
+        return array (  63 => 20,  55 => 16,  45 => 10,  42 => 9,  33 => 8,  25 => 7,  14 => 6,  7 => 1,);
+    }
+}

+ 1732 - 0
lib/markdown.php

@@ -0,0 +1,1732 @@
+<?php
+#
+# Markdown  -  A text-to-HTML conversion tool for web writers
+#
+# PHP Markdown
+# Copyright (c) 2004-2012 Michel Fortin  
+# <http://michelf.com/projects/php-markdown/>
+#
+# Original Markdown
+# Copyright (c) 2004-2006 John Gruber  
+# <http://daringfireball.net/projects/markdown/>
+#
+
+
+define( 'MARKDOWN_VERSION',  "1.0.1o" ); # Sun 8 Jan 2012
+
+
+#
+# Global default settings:
+#
+
+# Change to ">" for HTML output
+@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX',  " />");
+
+# Define the width of a tab for code blocks.
+@define( 'MARKDOWN_TAB_WIDTH',     4 );
+
+
+#
+# WordPress settings:
+#
+
+# Change to false to remove Markdown from posts and/or comments.
+@define( 'MARKDOWN_WP_POSTS',      true );
+@define( 'MARKDOWN_WP_COMMENTS',   true );
+
+
+
+### Standard Function Interface ###
+
+@define( 'MARKDOWN_PARSER_CLASS',  'Markdown_Parser' );
+
+function Markdown($text) {
+#
+# Initialize the parser and return the result of its transform method.
+#
+	# Setup static parser variable.
+	static $parser;
+	if (!isset($parser)) {
+		$parser_class = MARKDOWN_PARSER_CLASS;
+		$parser = new $parser_class;
+	}
+
+	# Transform text using parser.
+	return $parser->transform($text);
+}
+
+
+### WordPress Plugin Interface ###
+
+/*
+Plugin Name: Markdown
+Plugin URI: http://michelf.com/projects/php-markdown/
+Description: <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>
+Version: 1.0.1o
+Author: Michel Fortin
+Author URI: http://michelf.com/
+*/
+
+if (isset($wp_version)) {
+	# More details about how it works here:
+	# <http://michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
+	
+	# Post content and excerpts
+	# - Remove WordPress paragraph generator.
+	# - Run Markdown on excerpt, then remove all tags.
+	# - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
+	if (MARKDOWN_WP_POSTS) {
+		remove_filter('the_content',     'wpautop');
+        remove_filter('the_content_rss', 'wpautop');
+		remove_filter('the_excerpt',     'wpautop');
+		add_filter('the_content',     'Markdown', 6);
+        add_filter('the_content_rss', 'Markdown', 6);
+		add_filter('get_the_excerpt', 'Markdown', 6);
+		add_filter('get_the_excerpt', 'trim', 7);
+		add_filter('the_excerpt',     'mdwp_add_p');
+		add_filter('the_excerpt_rss', 'mdwp_strip_p');
+		
+		remove_filter('content_save_pre',  'balanceTags', 50);
+		remove_filter('excerpt_save_pre',  'balanceTags', 50);
+		add_filter('the_content',  	  'balanceTags', 50);
+		add_filter('get_the_excerpt', 'balanceTags', 9);
+	}
+	
+	# Comments
+	# - Remove WordPress paragraph generator.
+	# - Remove WordPress auto-link generator.
+	# - Scramble important tags before passing them to the kses filter.
+	# - Run Markdown on excerpt then remove paragraph tags.
+	if (MARKDOWN_WP_COMMENTS) {
+		remove_filter('comment_text', 'wpautop', 30);
+		remove_filter('comment_text', 'make_clickable');
+		add_filter('pre_comment_content', 'Markdown', 6);
+		add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
+		add_filter('pre_comment_content', 'mdwp_show_tags', 12);
+		add_filter('get_comment_text',    'Markdown', 6);
+		add_filter('get_comment_excerpt', 'Markdown', 6);
+		add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
+	
+		global $mdwp_hidden_tags, $mdwp_placeholders;
+		$mdwp_hidden_tags = explode(' ',
+			'<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>');
+		$mdwp_placeholders = explode(' ', str_rot13(
+			'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '.
+			'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli'));
+	}
+	
+	function mdwp_add_p($text) {
+		if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) {
+			$text = '<p>'.$text.'</p>';
+			$text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
+		}
+		return $text;
+	}
+	
+	function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
+
+	function mdwp_hide_tags($text) {
+		global $mdwp_hidden_tags, $mdwp_placeholders;
+		return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
+	}
+	function mdwp_show_tags($text) {
+		global $mdwp_hidden_tags, $mdwp_placeholders;
+		return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
+	}
+}
+
+
+### bBlog Plugin Info ###
+
+function identify_modifier_markdown() {
+	return array(
+		'name'			=> 'markdown',
+		'type'			=> 'modifier',
+		'nicename'		=> 'Markdown',
+		'description'	=> 'A text-to-HTML conversion tool for web writers',
+		'authors'		=> 'Michel Fortin and John Gruber',
+		'licence'		=> 'BSD-like',
+		'version'		=> MARKDOWN_VERSION,
+		'help'			=> '<a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>'
+	);
+}
+
+
+### Smarty Modifier Interface ###
+
+function smarty_modifier_markdown($text) {
+	return Markdown($text);
+}
+
+
+### Textile Compatibility Mode ###
+
+# Rename this file to "classTextile.php" and it can replace Textile everywhere.
+
+if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
+	# Try to include PHP SmartyPants. Should be in the same directory.
+	@include_once 'smartypants.php';
+	# Fake Textile class. It calls Markdown instead.
+	class Textile {
+		function TextileThis($text, $lite='', $encode='') {
+			if ($lite == '' && $encode == '')    $text = Markdown($text);
+			if (function_exists('SmartyPants'))  $text = SmartyPants($text);
+			return $text;
+		}
+		# Fake restricted version: restrictions are not supported for now.
+		function TextileRestricted($text, $lite='', $noimage='') {
+			return $this->TextileThis($text, $lite);
+		}
+		# Workaround to ensure compatibility with TextPattern 4.0.3.
+		function blockLite($text) { return $text; }
+	}
+}
+
+
+
+#
+# Markdown Parser Class
+#
+
+class Markdown_Parser {
+
+	# Regex to match balanced [brackets].
+	# Needed to insert a maximum bracked depth while converting to PHP.
+	var $nested_brackets_depth = 6;
+	var $nested_brackets_re;
+	
+	var $nested_url_parenthesis_depth = 4;
+	var $nested_url_parenthesis_re;
+
+	# Table of hash values for escaped characters:
+	var $escape_chars = '\`*_{}[]()>#+-.!';
+	var $escape_chars_re;
+
+	# Change to ">" for HTML output.
+	var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
+	var $tab_width = MARKDOWN_TAB_WIDTH;
+	
+	# Change to `true` to disallow markup or entities.
+	var $no_markup = false;
+	var $no_entities = false;
+	
+	# Predefined urls and titles for reference links and images.
+	var $predef_urls = array();
+	var $predef_titles = array();
+
+
+	function Markdown_Parser() {
+	#
+	# Constructor function. Initialize appropriate member variables.
+	#
+		$this->_initDetab();
+		$this->prepareItalicsAndBold();
+	
+		$this->nested_brackets_re = 
+			str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
+			str_repeat('\])*', $this->nested_brackets_depth);
+	
+		$this->nested_url_parenthesis_re = 
+			str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
+			str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
+		
+		$this->escape_chars_re = '['.preg_quote($this->escape_chars).']';
+		
+		# Sort document, block, and span gamut in ascendent priority order.
+		asort($this->document_gamut);
+		asort($this->block_gamut);
+		asort($this->span_gamut);
+	}
+
+
+	# Internal hashes used during transformation.
+	var $urls = array();
+	var $titles = array();
+	var $html_hashes = array();
+	
+	# Status flag to avoid invalid nesting.
+	var $in_anchor = false;
+	
+	
+	function setup() {
+	#
+	# Called before the transformation process starts to setup parser 
+	# states.
+	#
+		# Clear global hashes.
+		$this->urls = $this->predef_urls;
+		$this->titles = $this->predef_titles;
+		$this->html_hashes = array();
+		
+		$in_anchor = false;
+	}
+	
+	function teardown() {
+	#
+	# Called after the transformation process to clear any variable 
+	# which may be taking up memory unnecessarly.
+	#
+		$this->urls = array();
+		$this->titles = array();
+		$this->html_hashes = array();
+	}
+
+
+	function transform($text) {
+	#
+	# Main function. Performs some preprocessing on the input text
+	# and pass it through the document gamut.
+	#
+		$this->setup();
+	
+		# Remove UTF-8 BOM and marker character in input, if present.
+		$text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
+
+		# Standardize line endings:
+		#   DOS to Unix and Mac to Unix
+		$text = preg_replace('{\r\n?}', "\n", $text);
+
+		# Make sure $text ends with a couple of newlines:
+		$text .= "\n\n";
+
+		# Convert all tabs to spaces.
+		$text = $this->detab($text);
+
+		# Turn block-level HTML blocks into hash entries
+		$text = $this->hashHTMLBlocks($text);
+
+		# Strip any lines consisting only of spaces and tabs.
+		# This makes subsequent regexen easier to write, because we can
+		# match consecutive blank lines with /\n+/ instead of something
+		# contorted like /[ ]*\n+/ .
+		$text = preg_replace('/^[ ]+$/m', '', $text);
+
+		# Run document gamut methods.
+		foreach ($this->document_gamut as $method => $priority) {
+			$text = $this->$method($text);
+		}
+		
+		$this->teardown();
+
+		return $text . "\n";
+	}
+	
+	var $document_gamut = array(
+		# Strip link definitions, store in hashes.
+		"stripLinkDefinitions" => 20,
+		
+		"runBasicBlockGamut"   => 30,
+		);
+
+
+	function stripLinkDefinitions($text) {
+	#
+	# Strips link definitions from text, stores the URLs and titles in
+	# hash references.
+	#
+		$less_than_tab = $this->tab_width - 1;
+
+		# Link defs are in the form: ^[id]: url "optional title"
+		$text = preg_replace_callback('{
+							^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?:	# id = $1
+							  [ ]*
+							  \n?				# maybe *one* newline
+							  [ ]*
+							(?:
+							  <(.+?)>			# url = $2
+							|
+							  (\S+?)			# url = $3
+							)
+							  [ ]*
+							  \n?				# maybe one newline
+							  [ ]*
+							(?:
+								(?<=\s)			# lookbehind for whitespace
+								["(]
+								(.*?)			# title = $4
+								[")]
+								[ ]*
+							)?	# title is optional
+							(?:\n+|\Z)
+			}xm',
+			array(&$this, '_stripLinkDefinitions_callback'),
+			$text);
+		return $text;
+	}
+	function _stripLinkDefinitions_callback($matches) {
+		$link_id = strtolower($matches[1]);
+		$url = $matches[2] == '' ? $matches[3] : $matches[2];
+		$this->urls[$link_id] = $url;
+		$this->titles[$link_id] =& $matches[4];
+		return ''; # String that will replace the block
+	}
+
+
+	function hashHTMLBlocks($text) {
+		if ($this->no_markup)  return $text;
+
+		$less_than_tab = $this->tab_width - 1;
+
+		# Hashify HTML blocks:
+		# We only want to do this for block-level HTML tags, such as headers,
+		# lists, and tables. That's because we still want to wrap <p>s around
+		# "paragraphs" that are wrapped in non-block-level tags, such as anchors,
+		# phrase emphasis, and spans. The list of tags we're looking for is
+		# hard-coded:
+		#
+		# *  List "a" is made of tags which can be both inline or block-level.
+		#    These will be treated block-level when the start tag is alone on 
+		#    its line, otherwise they're not matched here and will be taken as 
+		#    inline later.
+		# *  List "b" is made of tags which are always block-level;
+		#
+		$block_tags_a_re = 'ins|del';
+		$block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
+						   'script|noscript|form|fieldset|iframe|math';
+
+		# Regular expression for the content of a block tag.
+		$nested_tags_level = 4;
+		$attr = '
+			(?>				# optional tag attributes
+			  \s			# starts with whitespace
+			  (?>
+				[^>"/]+		# text outside quotes
+			  |
+				/+(?!>)		# slash not followed by ">"
+			  |
+				"[^"]*"		# text inside double quotes (tolerate ">")
+			  |
+				\'[^\']*\'	# text inside single quotes (tolerate ">")
+			  )*
+			)?	
+			';
+		$content =
+			str_repeat('
+				(?>
+				  [^<]+			# content without tag
+				|
+				  <\2			# nested opening tag
+					'.$attr.'	# attributes
+					(?>
+					  />
+					|
+					  >', $nested_tags_level).	# end of opening tag
+					  '.*?'.					# last level nested tag content
+			str_repeat('
+					  </\2\s*>	# closing nested tag
+					)
+				  |				
+					<(?!/\2\s*>	# other tags with a different name
+				  )
+				)*',
+				$nested_tags_level);
+		$content2 = str_replace('\2', '\3', $content);
+
+		# First, look for nested blocks, e.g.:
+		# 	<div>
+		# 		<div>
+		# 		tags for inner block must be indented.
+		# 		</div>
+		# 	</div>
+		#
+		# The outermost tags must start at the left margin for this to match, and
+		# the inner nested divs must be indented.
+		# We need to do this before the next, more liberal match, because the next
+		# match will start at the first `<div>` and stop at the first `</div>`.
+		$text = preg_replace_callback('{(?>
+			(?>
+				(?<=\n\n)		# Starting after a blank line
+				|				# or
+				\A\n?			# the beginning of the doc
+			)
+			(						# save in $1
+
+			  # Match from `\n<tag>` to `</tag>\n`, handling nested tags 
+			  # in between.
+					
+						[ ]{0,'.$less_than_tab.'}
+						<('.$block_tags_b_re.')# start tag = $2
+						'.$attr.'>			# attributes followed by > and \n
+						'.$content.'		# content, support nesting
+						</\2>				# the matching end tag
+						[ ]*				# trailing spaces/tabs
+						(?=\n+|\Z)	# followed by a newline or end of document
+
+			| # Special version for tags of group a.
+
+						[ ]{0,'.$less_than_tab.'}
+						<('.$block_tags_a_re.')# start tag = $3
+						'.$attr.'>[ ]*\n	# attributes followed by >
+						'.$content2.'		# content, support nesting
+						</\3>				# the matching end tag
+						[ ]*				# trailing spaces/tabs
+						(?=\n+|\Z)	# followed by a newline or end of document
+					
+			| # Special case just for <hr />. It was easier to make a special 
+			  # case than to make the other regex more complicated.
+			
+						[ ]{0,'.$less_than_tab.'}
+						<(hr)				# start tag = $2
+						'.$attr.'			# attributes
+						/?>					# the matching end tag
+						[ ]*
+						(?=\n{2,}|\Z)		# followed by a blank line or end of document
+			
+			| # Special case for standalone HTML comments:
+			
+					[ ]{0,'.$less_than_tab.'}
+					(?s:
+						<!-- .*? -->
+					)
+					[ ]*
+					(?=\n{2,}|\Z)		# followed by a blank line or end of document
+			
+			| # PHP and ASP-style processor instructions (<? and <%)
+			
+					[ ]{0,'.$less_than_tab.'}
+					(?s:
+						<([?%])			# $2
+						.*?
+						\2>
+					)
+					[ ]*
+					(?=\n{2,}|\Z)		# followed by a blank line or end of document
+					
+			)
+			)}Sxmi',
+			array(&$this, '_hashHTMLBlocks_callback'),
+			$text);
+
+		return $text;
+	}
+	function _hashHTMLBlocks_callback($matches) {
+		$text = $matches[1];
+		$key  = $this->hashBlock($text);
+		return "\n\n$key\n\n";
+	}
+	
+	
+	function hashPart($text, $boundary = 'X') {
+	#
+	# Called whenever a tag must be hashed when a function insert an atomic 
+	# element in the text stream. Passing $text to through this function gives
+	# a unique text-token which will be reverted back when calling unhash.
+	#
+	# The $boundary argument specify what character should be used to surround
+	# the token. By convension, "B" is used for block elements that needs not
+	# to be wrapped into paragraph tags at the end, ":" is used for elements
+	# that are word separators and "X" is used in the general case.
+	#
+		# Swap back any tag hash found in $text so we do not have to `unhash`
+		# multiple times at the end.
+		$text = $this->unhash($text);
+		
+		# Then hash the block.
+		static $i = 0;
+		$key = "$boundary\x1A" . ++$i . $boundary;
+		$this->html_hashes[$key] = $text;
+		return $key; # String that will replace the tag.
+	}
+
+
+	function hashBlock($text) {
+	#
+	# Shortcut function for hashPart with block-level boundaries.
+	#
+		return $this->hashPart($text, 'B');
+	}
+
+
+	var $block_gamut = array(
+	#
+	# These are all the transformations that form block-level
+	# tags like paragraphs, headers, and list items.
+	#
+		"doHeaders"         => 10,
+		"doHorizontalRules" => 20,
+		
+		"doLists"           => 40,
+		"doCodeBlocks"      => 50,
+		"doBlockQuotes"     => 60,
+		);
+
+	function runBlockGamut($text) {
+	#
+	# Run block gamut tranformations.
+	#
+		# We need to escape raw HTML in Markdown source before doing anything 
+		# else. This need to be done for each block, and not only at the 
+		# begining in the Markdown function since hashed blocks can be part of
+		# list items and could have been indented. Indented blocks would have 
+		# been seen as a code block in a previous pass of hashHTMLBlocks.
+		$text = $this->hashHTMLBlocks($text);
+		
+		return $this->runBasicBlockGamut($text);
+	}
+	
+	function runBasicBlockGamut($text) {
+	#
+	# Run block gamut tranformations, without hashing HTML blocks. This is 
+	# useful when HTML blocks are known to be already hashed, like in the first
+	# whole-document pass.
+	#
+		foreach ($this->block_gamut as $method => $priority) {
+			$text = $this->$method($text);
+		}
+		
+		# Finally form paragraph and restore hashed blocks.
+		$text = $this->formParagraphs($text);
+
+		return $text;
+	}
+	
+	
+	function doHorizontalRules($text) {
+		# Do Horizontal Rules:
+		return preg_replace(
+			'{
+				^[ ]{0,3}	# Leading space
+				([-*_])		# $1: First marker
+				(?>			# Repeated marker group
+					[ ]{0,2}	# Zero, one, or two spaces.
+					\1			# Marker character
+				){2,}		# Group repeated at least twice
+				[ ]*		# Tailing spaces
+				$			# End of line.
+			}mx',
+			"\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n", 
+			$text);
+	}
+
+
+	var $span_gamut = array(
+	#
+	# These are all the transformations that occur *within* block-level
+	# tags like paragraphs, headers, and list items.
+	#
+		# Process character escapes, code spans, and inline HTML
+		# in one shot.
+		"parseSpan"           => -30,
+
+		# Process anchor and image tags. Images must come first,
+		# because ![foo][f] looks like an anchor.
+		"doImages"            =>  10,
+		"doAnchors"           =>  20,
+		
+		# Make links out of things like `<http://example.com/>`
+		# Must come after doAnchors, because you can use < and >
+		# delimiters in inline links like [this](<url>).
+		"doAutoLinks"         =>  30,
+		"encodeAmpsAndAngles" =>  40,
+
+		"doItalicsAndBold"    =>  50,
+		"doHardBreaks"        =>  60,
+		);
+
+	function runSpanGamut($text) {
+	#
+	# Run span gamut tranformations.
+	#
+		foreach ($this->span_gamut as $method => $priority) {
+			$text = $this->$method($text);
+		}
+
+		return $text;
+	}
+	
+	
+	function doHardBreaks($text) {
+		# Do hard breaks:
+		return preg_replace_callback('/ {2,}\n/', 
+			array(&$this, '_doHardBreaks_callback'), $text);
+	}
+	function _doHardBreaks_callback($matches) {
+		return $this->hashPart("<br$this->empty_element_suffix\n");
+	}
+
+
+	function doAnchors($text) {
+	#
+	# Turn Markdown link shortcuts into XHTML <a> tags.
+	#
+		if ($this->in_anchor) return $text;
+		$this->in_anchor = true;
+		
+		#
+		# First, handle reference-style links: [link text] [id]
+		#
+		$text = preg_replace_callback('{
+			(					# wrap whole match in $1
+			  \[
+				('.$this->nested_brackets_re.')	# link text = $2
+			  \]
+
+			  [ ]?				# one optional space
+			  (?:\n[ ]*)?		# one optional newline followed by spaces
+
+			  \[
+				(.*?)		# id = $3
+			  \]
+			)
+			}xs',
+			array(&$this, '_doAnchors_reference_callback'), $text);
+
+		#
+		# Next, inline-style links: [link text](url "optional title")
+		#
+		$text = preg_replace_callback('{
+			(				# wrap whole match in $1
+			  \[
+				('.$this->nested_brackets_re.')	# link text = $2
+			  \]
+			  \(			# literal paren
+				[ \n]*
+				(?:
+					<(.+?)>	# href = $3
+				|
+					('.$this->nested_url_parenthesis_re.')	# href = $4
+				)
+				[ \n]*
+				(			# $5
+				  ([\'"])	# quote char = $6
+				  (.*?)		# Title = $7
+				  \6		# matching quote
+				  [ \n]*	# ignore any spaces/tabs between closing quote and )
+				)?			# title is optional
+			  \)
+			)
+			}xs',
+			array(&$this, '_doAnchors_inline_callback'), $text);
+
+		#
+		# Last, handle reference-style shortcuts: [link text]
+		# These must come last in case you've also got [link text][1]
+		# or [link text](/foo)
+		#
+		$text = preg_replace_callback('{
+			(					# wrap whole match in $1
+			  \[
+				([^\[\]]+)		# link text = $2; can\'t contain [ or ]
+			  \]
+			)
+			}xs',
+			array(&$this, '_doAnchors_reference_callback'), $text);
+
+		$this->in_anchor = false;
+		return $text;
+	}
+	function _doAnchors_reference_callback($matches) {
+		$whole_match =  $matches[1];
+		$link_text   =  $matches[2];
+		$link_id     =& $matches[3];
+
+		if ($link_id == "") {
+			# for shortcut links like [this][] or [this].
+			$link_id = $link_text;
+		}
+		
+		# lower-case and turn embedded newlines into spaces
+		$link_id = strtolower($link_id);
+		$link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
+
+		if (isset($this->urls[$link_id])) {
+			$url = $this->urls[$link_id];
+			$url = $this->encodeAttribute($url);
+			
+			$result = "<a href=\"$url\"";
+			if ( isset( $this->titles[$link_id] ) ) {
+				$title = $this->titles[$link_id];
+				$title = $this->encodeAttribute($title);
+				$result .=  " title=\"$title\"";
+			}
+		
+			$link_text = $this->runSpanGamut($link_text);
+			$result .= ">$link_text</a>";
+			$result = $this->hashPart($result);
+		}
+		else {
+			$result = $whole_match;
+		}
+		return $result;
+	}
+	function _doAnchors_inline_callback($matches) {
+		$whole_match	=  $matches[1];
+		$link_text		=  $this->runSpanGamut($matches[2]);
+		$url			=  $matches[3] == '' ? $matches[4] : $matches[3];
+		$title			=& $matches[7];
+
+		$url = $this->encodeAttribute($url);
+
+		$result = "<a href=\"$url\"";
+		if (isset($title)) {
+			$title = $this->encodeAttribute($title);
+			$result .=  " title=\"$title\"";
+		}
+		
+		$link_text = $this->runSpanGamut($link_text);
+		$result .= ">$link_text</a>";
+
+		return $this->hashPart($result);
+	}
+
+
+	function doImages($text) {
+	#
+	# Turn Markdown image shortcuts into <img> tags.
+	#
+		#
+		# First, handle reference-style labeled images: ![alt text][id]
+		#
+		$text = preg_replace_callback('{
+			(				# wrap whole match in $1
+			  !\[
+				('.$this->nested_brackets_re.')		# alt text = $2
+			  \]
+
+			  [ ]?				# one optional space
+			  (?:\n[ ]*)?		# one optional newline followed by spaces
+
+			  \[
+				(.*?)		# id = $3
+			  \]
+
+			)
+			}xs', 
+			array(&$this, '_doImages_reference_callback'), $text);
+
+		#
+		# Next, handle inline images:  ![alt text](url "optional title")
+		# Don't forget: encode * and _
+		#
+		$text = preg_replace_callback('{
+			(				# wrap whole match in $1
+			  !\[
+				('.$this->nested_brackets_re.')		# alt text = $2
+			  \]
+			  \s?			# One optional whitespace character
+			  \(			# literal paren
+				[ \n]*
+				(?:
+					<(\S*)>	# src url = $3
+				|
+					('.$this->nested_url_parenthesis_re.')	# src url = $4
+				)
+				[ \n]*
+				(			# $5
+				  ([\'"])	# quote char = $6
+				  (.*?)		# title = $7
+				  \6		# matching quote
+				  [ \n]*
+				)?			# title is optional
+			  \)
+			)
+			}xs',
+			array(&$this, '_doImages_inline_callback'), $text);
+
+		return $text;
+	}
+	function _doImages_reference_callback($matches) {
+		$whole_match = $matches[1];
+		$alt_text    = $matches[2];
+		$link_id     = strtolower($matches[3]);
+
+		if ($link_id == "") {
+			$link_id = strtolower($alt_text); # for shortcut links like ![this][].
+		}
+
+		$alt_text = $this->encodeAttribute($alt_text);
+		if (isset($this->urls[$link_id])) {
+			$url = $this->encodeAttribute($this->urls[$link_id]);
+			$result = "<img src=\"$url\" alt=\"$alt_text\"";
+			if (isset($this->titles[$link_id])) {
+				$title = $this->titles[$link_id];
+				$title = $this->encodeAttribute($title);
+				$result .=  " title=\"$title\"";
+			}
+			$result .= $this->empty_element_suffix;
+			$result = $this->hashPart($result);
+		}
+		else {
+			# If there's no such link ID, leave intact:
+			$result = $whole_match;
+		}
+
+		return $result;
+	}
+	function _doImages_inline_callback($matches) {
+		$whole_match	= $matches[1];
+		$alt_text		= $matches[2];
+		$url			= $matches[3] == '' ? $matches[4] : $matches[3];
+		$title			=& $matches[7];
+
+		$alt_text = $this->encodeAttribute($alt_text);
+		$url = $this->encodeAttribute($url);
+		$result = "<img src=\"$url\" alt=\"$alt_text\"";
+		if (isset($title)) {
+			$title = $this->encodeAttribute($title);
+			$result .=  " title=\"$title\""; # $title already quoted
+		}
+		$result .= $this->empty_element_suffix;
+
+		return $this->hashPart($result);
+	}
+
+
+	function doHeaders($text) {
+		# Setext-style headers:
+		#	  Header 1
+		#	  ========
+		#  
+		#	  Header 2
+		#	  --------
+		#
+		$text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
+			array(&$this, '_doHeaders_callback_setext'), $text);
+
+		# atx-style headers:
+		#	# Header 1
+		#	## Header 2
+		#	## Header 2 with closing hashes ##
+		#	...
+		#	###### Header 6
+		#
+		$text = preg_replace_callback('{
+				^(\#{1,6})	# $1 = string of #\'s
+				[ ]*
+				(.+?)		# $2 = Header text
+				[ ]*
+				\#*			# optional closing #\'s (not counted)
+				\n+
+			}xm',
+			array(&$this, '_doHeaders_callback_atx'), $text);
+
+		return $text;
+	}
+	function _doHeaders_callback_setext($matches) {
+		# Terrible hack to check we haven't found an empty list item.
+		if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
+			return $matches[0];
+		
+		$level = $matches[2]{0} == '=' ? 1 : 2;
+		$block = "<h$level>".$this->runSpanGamut($matches[1])."</h$level>";
+		return "\n" . $this->hashBlock($block) . "\n\n";
+	}
+	function _doHeaders_callback_atx($matches) {
+		$level = strlen($matches[1]);
+		$block = "<h$level>".$this->runSpanGamut($matches[2])."</h$level>";
+		return "\n" . $this->hashBlock($block) . "\n\n";
+	}
+
+
+	function doLists($text) {
+	#
+	# Form HTML ordered (numbered) and unordered (bulleted) lists.
+	#
+		$less_than_tab = $this->tab_width - 1;
+
+		# Re-usable patterns to match list item bullets and number markers:
+		$marker_ul_re  = '[*+-]';
+		$marker_ol_re  = '\d+[\.]';
+		$marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
+
+		$markers_relist = array(
+			$marker_ul_re => $marker_ol_re,
+			$marker_ol_re => $marker_ul_re,
+			);
+
+		foreach ($markers_relist as $marker_re => $other_marker_re) {
+			# Re-usable pattern to match any entirel ul or ol list:
+			$whole_list_re = '
+				(								# $1 = whole list
+				  (								# $2
+					([ ]{0,'.$less_than_tab.'})	# $3 = number of spaces
+					('.$marker_re.')			# $4 = first list item marker
+					[ ]+
+				  )
+				  (?s:.+?)
+				  (								# $5
+					  \z
+					|
+					  \n{2,}
+					  (?=\S)
+					  (?!						# Negative lookahead for another list item marker
+						[ ]*
+						'.$marker_re.'[ ]+
+					  )
+					|
+					  (?=						# Lookahead for another kind of list
+					    \n
+						\3						# Must have the same indentation
+						'.$other_marker_re.'[ ]+
+					  )
+				  )
+				)
+			'; // mx
+			
+			# We use a different prefix before nested lists than top-level lists.
+			# See extended comment in _ProcessListItems().
+		
+			if ($this->list_level) {
+				$text = preg_replace_callback('{
+						^
+						'.$whole_list_re.'
+					}mx',
+					array(&$this, '_doLists_callback'), $text);
+			}
+			else {
+				$text = preg_replace_callback('{
+						(?:(?<=\n)\n|\A\n?) # Must eat the newline
+						'.$whole_list_re.'
+					}mx',
+					array(&$this, '_doLists_callback'), $text);
+			}
+		}
+
+		return $text;
+	}
+	function _doLists_callback($matches) {
+		# Re-usable patterns to match list item bullets and number markers:
+		$marker_ul_re  = '[*+-]';
+		$marker_ol_re  = '\d+[\.]';
+		$marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
+		
+		$list = $matches[1];
+		$list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
+		
+		$marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re );
+		
+		$list .= "\n";
+		$result = $this->processListItems($list, $marker_any_re);
+		
+		$result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
+		return "\n". $result ."\n\n";
+	}
+
+	var $list_level = 0;
+
+	function processListItems($list_str, $marker_any_re) {
+	#
+	#	Process the contents of a single ordered or unordered list, splitting it
+	#	into individual list items.
+	#
+		# The $this->list_level global keeps track of when we're inside a list.
+		# Each time we enter a list, we increment it; when we leave a list,
+		# we decrement. If it's zero, we're not in a list anymore.
+		#
+		# We do this because when we're not inside a list, we want to treat
+		# something like this:
+		#
+		#		I recommend upgrading to version
+		#		8. Oops, now this line is treated
+		#		as a sub-list.
+		#
+		# As a single paragraph, despite the fact that the second line starts
+		# with a digit-period-space sequence.
+		#
+		# Whereas when we're inside a list (or sub-list), that line will be
+		# treated as the start of a sub-list. What a kludge, huh? This is
+		# an aspect of Markdown's syntax that's hard to parse perfectly
+		# without resorting to mind-reading. Perhaps the solution is to
+		# change the syntax rules such that sub-lists must start with a
+		# starting cardinal number; e.g. "1." or "a.".
+		
+		$this->list_level++;
+
+		# trim trailing blank lines:
+		$list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
+
+		$list_str = preg_replace_callback('{
+			(\n)?							# leading line = $1
+			(^[ ]*)							# leading whitespace = $2
+			('.$marker_any_re.'				# list marker and space = $3
+				(?:[ ]+|(?=\n))	# space only required if item is not empty
+			)
+			((?s:.*?))						# list item text   = $4
+			(?:(\n+(?=\n))|\n)				# tailing blank line = $5
+			(?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
+			}xm',
+			array(&$this, '_processListItems_callback'), $list_str);
+
+		$this->list_level--;
+		return $list_str;
+	}
+	function _processListItems_callback($matches) {
+		$item = $matches[4];
+		$leading_line =& $matches[1];
+		$leading_space =& $matches[2];
+		$marker_space = $matches[3];
+		$tailing_blank_line =& $matches[5];
+
+		if ($leading_line || $tailing_blank_line || 
+			preg_match('/\n{2,}/', $item))
+		{
+			# Replace marker with the appropriate whitespace indentation
+			$item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
+			$item = $this->runBlockGamut($this->outdent($item)."\n");
+		}
+		else {
+			# Recursion for sub-lists:
+			$item = $this->doLists($this->outdent($item));
+			$item = preg_replace('/\n+$/', '', $item);
+			$item = $this->runSpanGamut($item);
+		}
+
+		return "<li>" . $item . "</li>\n";
+	}
+
+
+	function doCodeBlocks($text) {
+	#
+	#	Process Markdown `<pre><code>` blocks.
+	#
+		$text = preg_replace_callback('{
+				(?:\n\n|\A\n?)
+				(	            # $1 = the code block -- one or more lines, starting with a space/tab
+				  (?>
+					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
+					.*\n+
+				  )+
+				)
+				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
+			}xm',
+			array(&$this, '_doCodeBlocks_callback'), $text);
+
+		return $text;
+	}
+	function _doCodeBlocks_callback($matches) {
+		$codeblock = $matches[1];
+
+		$codeblock = $this->outdent($codeblock);
+		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
+
+		# trim leading newlines and trailing newlines
+		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
+
+		$codeblock = "<pre><code>$codeblock\n</code></pre>";
+		return "\n\n".$this->hashBlock($codeblock)."\n\n";
+	}
+
+
+	function makeCodeSpan($code) {
+	#
+	# Create a code span markup for $code. Called from handleSpanToken.
+	#
+		$code = htmlspecialchars(trim($code), ENT_NOQUOTES);
+		return $this->hashPart("<code>$code</code>");
+	}
+
+
+	var $em_relist = array(
+		''  => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![\.,:;]\s)',
+		'*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
+		'_' => '(?<=\S|^)(?<!_)_(?!_)',
+		);
+	var $strong_relist = array(
+		''   => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![\.,:;]\s)',
+		'**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
+		'__' => '(?<=\S|^)(?<!_)__(?!_)',
+		);
+	var $em_strong_relist = array(
+		''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![\.,:;]\s)',
+		'***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
+		'___' => '(?<=\S|^)(?<!_)___(?!_)',
+		);
+	var $em_strong_prepared_relist;
+	
+	function prepareItalicsAndBold() {
+	#
+	# Prepare regular expressions for searching emphasis tokens in any
+	# context.
+	#
+		foreach ($this->em_relist as $em => $em_re) {
+			foreach ($this->strong_relist as $strong => $strong_re) {
+				# Construct list of allowed token expressions.
+				$token_relist = array();
+				if (isset($this->em_strong_relist["$em$strong"])) {
+					$token_relist[] = $this->em_strong_relist["$em$strong"];
+				}
+				$token_relist[] = $em_re;
+				$token_relist[] = $strong_re;
+				
+				# Construct master expression from list.
+				$token_re = '{('. implode('|', $token_relist) .')}';
+				$this->em_strong_prepared_relist["$em$strong"] = $token_re;
+			}
+		}
+	}
+	
+	function doItalicsAndBold($text) {
+		$token_stack = array('');
+		$text_stack = array('');
+		$em = '';
+		$strong = '';
+		$tree_char_em = false;
+		
+		while (1) {
+			#
+			# Get prepared regular expression for seraching emphasis tokens
+			# in current context.
+			#
+			$token_re = $this->em_strong_prepared_relist["$em$strong"];
+			
+			#
+			# Each loop iteration search for the next emphasis token. 
+			# Each token is then passed to handleSpanToken.
+			#
+			$parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
+			$text_stack[0] .= $parts[0];
+			$token =& $parts[1];
+			$text =& $parts[2];
+			
+			if (empty($token)) {
+				# Reached end of text span: empty stack without emitting.
+				# any more emphasis.
+				while ($token_stack[0]) {
+					$text_stack[1] .= array_shift($token_stack);
+					$text_stack[0] .= array_shift($text_stack);
+				}
+				break;
+			}
+			
+			$token_len = strlen($token);
+			if ($tree_char_em) {
+				# Reached closing marker while inside a three-char emphasis.
+				if ($token_len == 3) {
+					# Three-char closing marker, close em and strong.
+					array_shift($token_stack);
+					$span = array_shift($text_stack);
+					$span = $this->runSpanGamut($span);
+					$span = "<strong><em>$span</em></strong>";
+					$text_stack[0] .= $this->hashPart($span);
+					$em = '';
+					$strong = '';
+				} else {
+					# Other closing marker: close one em or strong and
+					# change current token state to match the other
+					$token_stack[0] = str_repeat($token{0}, 3-$token_len);
+					$tag = $token_len == 2 ? "strong" : "em";
+					$span = $text_stack[0];
+					$span = $this->runSpanGamut($span);
+					$span = "<$tag>$span</$tag>";
+					$text_stack[0] = $this->hashPart($span);
+					$$tag = ''; # $$tag stands for $em or $strong
+				}
+				$tree_char_em = false;
+			} else if ($token_len == 3) {
+				if ($em) {
+					# Reached closing marker for both em and strong.
+					# Closing strong marker:
+					for ($i = 0; $i < 2; ++$i) {
+						$shifted_token = array_shift($token_stack);
+						$tag = strlen($shifted_token) == 2 ? "strong" : "em";
+						$span = array_shift($text_stack);
+						$span = $this->runSpanGamut($span);
+						$span = "<$tag>$span</$tag>";
+						$text_stack[0] .= $this->hashPart($span);
+						$$tag = ''; # $$tag stands for $em or $strong
+					}
+				} else {
+					# Reached opening three-char emphasis marker. Push on token 
+					# stack; will be handled by the special condition above.
+					$em = $token{0};
+					$strong = "$em$em";
+					array_unshift($token_stack, $token);
+					array_unshift($text_stack, '');
+					$tree_char_em = true;
+				}
+			} else if ($token_len == 2) {
+				if ($strong) {
+					# Unwind any dangling emphasis marker:
+					if (strlen($token_stack[0]) == 1) {
+						$text_stack[1] .= array_shift($token_stack);
+						$text_stack[0] .= array_shift($text_stack);
+					}
+					# Closing strong marker:
+					array_shift($token_stack);
+					$span = array_shift($text_stack);
+					$span = $this->runSpanGamut($span);
+					$span = "<strong>$span</strong>";
+					$text_stack[0] .= $this->hashPart($span);
+					$strong = '';
+				} else {
+					array_unshift($token_stack, $token);
+					array_unshift($text_stack, '');
+					$strong = $token;
+				}
+			} else {
+				# Here $token_len == 1
+				if ($em) {
+					if (strlen($token_stack[0]) == 1) {
+						# Closing emphasis marker:
+						array_shift($token_stack);
+						$span = array_shift($text_stack);
+						$span = $this->runSpanGamut($span);
+						$span = "<em>$span</em>";
+						$text_stack[0] .= $this->hashPart($span);
+						$em = '';
+					} else {
+						$text_stack[0] .= $token;
+					}
+				} else {
+					array_unshift($token_stack, $token);
+					array_unshift($text_stack, '');
+					$em = $token;
+				}
+			}
+		}
+		return $text_stack[0];
+	}
+
+
+	function doBlockQuotes($text) {
+		$text = preg_replace_callback('/
+			  (								# Wrap whole match in $1
+				(?>
+				  ^[ ]*>[ ]?			# ">" at the start of a line
+					.+\n					# rest of the first line
+				  (.+\n)*					# subsequent consecutive lines
+				  \n*						# blanks
+				)+
+			  )
+			/xm',
+			array(&$this, '_doBlockQuotes_callback'), $text);
+
+		return $text;
+	}
+	function _doBlockQuotes_callback($matches) {
+		$bq = $matches[1];
+		# trim one level of quoting - trim whitespace-only lines
+		$bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
+		$bq = $this->runBlockGamut($bq);		# recurse
+
+		$bq = preg_replace('/^/m', "  ", $bq);
+		# These leading spaces cause problem with <pre> content, 
+		# so we need to fix that:
+		$bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', 
+			array(&$this, '_doBlockQuotes_callback2'), $bq);
+
+		return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
+	}
+	function _doBlockQuotes_callback2($matches) {
+		$pre = $matches[1];
+		$pre = preg_replace('/^  /m', '', $pre);
+		return $pre;
+	}
+
+
+	function formParagraphs($text) {
+	#
+	#	Params:
+	#		$text - string to process with html <p> tags
+	#
+		# Strip leading and trailing lines:
+		$text = preg_replace('/\A\n+|\n+\z/', '', $text);
+
+		$grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
+
+		#
+		# Wrap <p> tags and unhashify HTML blocks
+		#
+		foreach ($grafs as $key => $value) {
+			if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
+				# Is a paragraph.
+				$value = $this->runSpanGamut($value);
+				$value = preg_replace('/^([ ]*)/', "<p>", $value);
+				$value .= "</p>";
+				$grafs[$key] = $this->unhash($value);
+			}
+			else {
+				# Is a block.
+				# Modify elements of @grafs in-place...
+				$graf = $value;
+				$block = $this->html_hashes[$graf];
+				$graf = $block;
+//				if (preg_match('{
+//					\A
+//					(							# $1 = <div> tag
+//					  <div  \s+
+//					  [^>]*
+//					  \b
+//					  markdown\s*=\s*  ([\'"])	#	$2 = attr quote char
+//					  1
+//					  \2
+//					  [^>]*
+//					  >
+//					)
+//					(							# $3 = contents
+//					.*
+//					)
+//					(</div>)					# $4 = closing tag
+//					\z
+//					}xs', $block, $matches))
+//				{
+//					list(, $div_open, , $div_content, $div_close) = $matches;
+//
+//					# We can't call Markdown(), because that resets the hash;
+//					# that initialization code should be pulled into its own sub, though.
+//					$div_content = $this->hashHTMLBlocks($div_content);
+//					
+//					# Run document gamut methods on the content.
+//					foreach ($this->document_gamut as $method => $priority) {
+//						$div_content = $this->$method($div_content);
+//					}
+//
+//					$div_open = preg_replace(
+//						'{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
+//
+//					$graf = $div_open . "\n" . $div_content . "\n" . $div_close;
+//				}
+				$grafs[$key] = $graf;
+			}
+		}
+
+		return implode("\n\n", $grafs);
+	}
+
+
+	function encodeAttribute($text) {
+	#
+	# Encode text for a double-quoted HTML attribute. This function
+	# is *not* suitable for attributes enclosed in single quotes.
+	#
+		$text = $this->encodeAmpsAndAngles($text);
+		$text = str_replace('"', '&quot;', $text);
+		return $text;
+	}
+	
+	
+	function encodeAmpsAndAngles($text) {
+	#
+	# Smart processing for ampersands and angle brackets that need to 
+	# be encoded. Valid character entities are left alone unless the
+	# no-entities mode is set.
+	#
+		if ($this->no_entities) {
+			$text = str_replace('&', '&amp;', $text);
+		} else {
+			# Ampersand-encoding based entirely on Nat Irons's Amputator
+			# MT plugin: <http://bumppo.net/projects/amputator/>
+			$text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', 
+								'&amp;', $text);;
+		}
+		# Encode remaining <'s
+		$text = str_replace('<', '&lt;', $text);
+
+		return $text;
+	}
+
+
+	function doAutoLinks($text) {
+		$text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', 
+			array(&$this, '_doAutoLinks_url_callback'), $text);
+
+		# Email addresses: <address@domain.foo>
+		$text = preg_replace_callback('{
+			<
+			(?:mailto:)?
+			(
+				(?:
+					[-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
+				|
+					".*?"
+				)
+				\@
+				(?:
+					[-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
+				|
+					\[[\d.a-fA-F:]+\]	# IPv4 & IPv6
+				)
+			)
+			>
+			}xi',
+			array(&$this, '_doAutoLinks_email_callback'), $text);
+
+		return $text;
+	}
+	function _doAutoLinks_url_callback($matches) {
+		$url = $this->encodeAttribute($matches[1]);
+		$link = "<a href=\"$url\">$url</a>";
+		return $this->hashPart($link);
+	}
+	function _doAutoLinks_email_callback($matches) {
+		$address = $matches[1];
+		$link = $this->encodeEmailAddress($address);
+		return $this->hashPart($link);
+	}
+
+
+	function encodeEmailAddress($addr) {
+	#
+	#	Input: an email address, e.g. "foo@example.com"
+	#
+	#	Output: the email address as a mailto link, with each character
+	#		of the address encoded as either a decimal or hex entity, in
+	#		the hopes of foiling most address harvesting spam bots. E.g.:
+	#
+	#	  <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
+	#        &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
+	#        &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
+	#        &#101;&#46;&#x63;&#111;&#x6d;</a></p>
+	#
+	#	Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
+	#   With some optimizations by Milian Wolff.
+	#
+		$addr = "mailto:" . $addr;
+		$chars = preg_split('/(?<!^)(?!$)/', $addr);
+		$seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
+		
+		foreach ($chars as $key => $char) {
+			$ord = ord($char);
+			# Ignore non-ascii chars.
+			if ($ord < 128) {
+				$r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
+				# roughly 10% raw, 45% hex, 45% dec
+				# '@' *must* be encoded. I insist.
+				if ($r > 90 && $char != '@') /* do nothing */;
+				else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';';
+				else              $chars[$key] = '&#'.$ord.';';
+			}
+		}
+		
+		$addr = implode('', $chars);
+		$text = implode('', array_slice($chars, 7)); # text without `mailto:`
+		$addr = "<a href=\"$addr\">$text</a>";
+
+		return $addr;
+	}
+
+
+	function parseSpan($str) {
+	#
+	# Take the string $str and parse it into tokens, hashing embeded HTML,
+	# escaped characters and handling code spans.
+	#
+		$output = '';
+		
+		$span_re = '{
+				(
+					\\\\'.$this->escape_chars_re.'
+				|
+					(?<![`\\\\])
+					`+						# code span marker
+			'.( $this->no_markup ? '' : '
+				|
+					<!--    .*?     -->		# comment
+				|
+					<\?.*?\?> | <%.*?%>		# processing instruction
+				|
+					<[/!$]?[-a-zA-Z0-9:_]+	# regular tags
+					(?>
+						\s
+						(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
+					)?
+					>
+			').'
+				)
+				}xs';
+
+		while (1) {
+			#
+			# Each loop iteration seach for either the next tag, the next 
+			# openning code span marker, or the next escaped character. 
+			# Each token is then passed to handleSpanToken.
+			#
+			$parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
+			
+			# Create token from text preceding tag.
+			if ($parts[0] != "") {
+				$output .= $parts[0];
+			}
+			
+			# Check if we reach the end.
+			if (isset($parts[1])) {
+				$output .= $this->handleSpanToken($parts[1], $parts[2]);
+				$str = $parts[2];
+			}
+			else {
+				break;
+			}
+		}
+		
+		return $output;
+	}
+	
+	
+	function handleSpanToken($token, &$str) {
+	#
+	# Handle $token provided by parseSpan by determining its nature and 
+	# returning the corresponding value that should replace it.
+	#
+		switch ($token{0}) {
+			case "\\":
+				return $this->hashPart("&#". ord($token{1}). ";");
+			case "`":
+				# Search for end marker in remaining text.
+				if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', 
+					$str, $matches))
+				{
+					$str = $matches[2];
+					$codespan = $this->makeCodeSpan($matches[1]);
+					return $this->hashPart($codespan);
+				}
+				return $token; // return as text since no ending marker found.
+			default:
+				return $this->hashPart($token);
+		}
+	}
+
+
+	function outdent($text) {
+	#
+	# Remove one level of line-leading tabs or spaces
+	#
+		return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
+	}
+
+
+	# String length function for detab. `_initDetab` will create a function to 
+	# hanlde UTF-8 if the default function does not exist.
+	var $utf8_strlen = 'mb_strlen';
+	
+	function detab($text) {
+	#
+	# Replace tabs with the appropriate amount of space.
+	#
+		# For each line we separate the line in blocks delemited by
+		# tab characters. Then we reconstruct every line by adding the 
+		# appropriate number of space between each blocks.
+		
+		$text = preg_replace_callback('/^.*\t.*$/m',
+			array(&$this, '_detab_callback'), $text);
+
+		return $text;
+	}
+	function _detab_callback($matches) {
+		$line = $matches[0];
+		$strlen = $this->utf8_strlen; # strlen function for UTF-8.
+		
+		# Split in blocks.
+		$blocks = explode("\t", $line);
+		# Add each blocks to the line.
+		$line = $blocks[0];
+		unset($blocks[0]); # Do not add first block twice.
+		foreach ($blocks as $block) {
+			# Calculate amount of space, insert spaces, insert block.
+			$amount = $this->tab_width - 
+				$strlen($line, 'UTF-8') % $this->tab_width;
+			$line .= str_repeat(" ", $amount) . $block;
+		}
+		return $line;
+	}
+	function _initDetab() {
+	#
+	# Check for the availability of the function in the `utf8_strlen` property
+	# (initially `mb_strlen`). If the function is not available, create a 
+	# function that will loosely count the number of UTF-8 characters with a
+	# regular expression.
+	#
+		if (function_exists($this->utf8_strlen)) return;
+		$this->utf8_strlen = create_function('$text', 'return preg_match_all(
+			"/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", 
+			$text, $m);');
+	}
+
+
+	function unhash($text) {
+	#
+	# Swap back in all the tags hashed by _HashHTMLBlocks.
+	#
+		return preg_replace_callback('/(.)\x1A[0-9]+\1/', 
+			array(&$this, '_unhash_callback'), $text);
+	}
+	function _unhash_callback($matches) {
+		return $this->html_hashes[$matches[0]];
+	}
+
+}
+
+/*
+
+PHP Markdown
+============
+
+Description
+-----------
+
+This is a PHP translation of the original Markdown formatter written in
+Perl by John Gruber.
+
+Markdown is a text-to-HTML filter; it translates an easy-to-read /
+easy-to-write structured text format into HTML. Markdown's text format
+is most similar to that of plain text email, and supports features such
+as headers, *emphasis*, code blocks, blockquotes, and links.
+
+Markdown's syntax is designed not as a generic markup language, but
+specifically to serve as a front-end to (X)HTML. You can use span-level
+HTML tags anywhere in a Markdown document, and you can use block level
+HTML tags (like <div> and <table> as well).
+
+For more information about Markdown's syntax, see:
+
+<http://daringfireball.net/projects/markdown/>
+
+
+Bugs
+----
+
+To file bug reports please send email to:
+
+<michel.fortin@michelf.com>
+
+Please include with your report: (1) the example input; (2) the output you
+expected; (3) the output Markdown actually produced.
+
+
+Version History
+--------------- 
+
+See the readme file for detailed release notes for this version.
+
+
+Copyright and License
+---------------------
+
+PHP Markdown
+Copyright (c) 2004-2009 Michel Fortin  
+<http://michelf.com/>  
+All rights reserved.
+
+Based on Markdown
+Copyright (c) 2003-2006 John Gruber   
+<http://daringfireball.net/>   
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+*	Redistributions of source code must retain the above copyright notice,
+	this list of conditions and the following disclaimer.
+
+*	Redistributions in binary form must reproduce the above copyright
+	notice, this list of conditions and the following disclaimer in the
+	documentation and/or other materials provided with the distribution.
+
+*	Neither the name "Markdown" nor the names of its contributors may
+	be used to endorse or promote products derived from this software
+	without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as
+is" and any express or implied warranties, including, but not limited
+to, the implied warranties of merchantability and fitness for a
+particular purpose are disclaimed. In no event shall the copyright owner
+or contributors be liable for any direct, indirect, incidental, special,
+exemplary, or consequential damages (including, but not limited to,
+procurement of substitute goods or services; loss of use, data, or
+profits; or business interruption) however caused and on any theory of
+liability, whether in contract, strict liability, or tort (including
+negligence or otherwise) arising in any way out of the use of this
+software, even if advised of the possibility of such damage.
+
+*/
+?>

+ 114 - 0
lib/pico.php

@@ -0,0 +1,114 @@
+<?php 
+
+class Pico {
+
+	function __construct()
+	{
+		
+
+		// Get request url and script url
+		$url = '';
+		$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
+		$script_url  = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
+			
+		// Get our url path and trim the / of the left and the right
+		if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/');
+
+		// Get the file path
+		if($url) $file = strtolower(CONTENT_DIR . $url);
+		else $file = CONTENT_DIR .'index';
+
+		// Load the file
+		if(is_dir($file)) $file = CONTENT_DIR . $url .'/index.txt';
+		else $file .= '.txt';
+
+		if(file_exists($file)) $content = file_get_contents($file);
+		else $content = file_get_contents(CONTENT_DIR .'404.txt');
+
+		$meta = $this->read_file_meta($content);
+		$content = preg_replace('#/\*.+?\*/#s', '', $content); // Remove comments and meta
+		$content = $this->parse_content($content);
+
+		// Load the settings
+		$settings = $this->get_config();
+		$env = array('autoescape' => false);
+		if($settings['enable_cache']) $env['cache'] = CACHE_DIR;
+		
+		// Load the theme
+		Twig_Autoloader::register();
+		$loader = new Twig_Loader_Filesystem(THEMES_DIR . $settings['theme']);
+		$twig = new Twig_Environment($loader, $env);
+		echo $twig->render('index.html', array(
+			'config' => $settings,
+			'base_dir' => rtrim(ROOT_DIR, '/'),
+			'base_url' => $settings['base_url'],
+			'theme_dir' => THEMES_DIR . $settings['theme'],
+			'theme_url' => $settings['base_url'] .'/'. basename(THEMES_DIR) .'/'. $settings['theme'],
+			'site_title' => $settings['site_title'],
+			'meta' => $meta,
+			'content' => $content
+		));
+	}
+
+	function parse_content($content)
+	{
+		$content = str_replace('%base_url%', $this->base_url(), $content);
+		$content = Markdown($content);
+
+		return $content;
+	}
+
+	function read_file_meta($content)
+	{
+		$headers = array(
+			'title'       => 'Title',
+			'description' => 'Description',
+			'robots'      => 'Robots'
+		);
+
+	 	foreach ($headers as $field => $regex){
+			if (preg_match('/^[ \t\/*#@]*' . preg_quote($regex, '/') . ':(.*)$/mi', $content, $match) && $match[1]){
+				$headers[ $field ] = trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $match[1]));
+			} else {
+				$headers[ $field ] = '';
+			}
+		}
+
+		return $headers;
+	}
+
+	function get_config()
+	{
+		global $config;
+
+		$defaults = array(
+			'site_title' => 'Pico',
+			'base_url' => $this->base_url(),
+			'theme' => 'default',
+			'enable_cache' => false
+		);
+
+		foreach($defaults as $key=>$val){
+			if(isset($config[$key]) && $config[$key]) $defaults[$key] = $config[$key];
+		}
+
+		return $defaults;
+	}
+
+	function base_url()
+	{
+		global $config;
+		if(isset($config['base_url']) && $config['base_url']) return $config['base_url'];
+
+		$url = '';
+		$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
+		$script_url  = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
+		if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/');
+
+		$protocol = $_SERVER['HTTPS'] ? "https" : "http";
+		return rtrim(str_replace($url, '', $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']), '/');
+	}
+
+}
+
+?>

+ 9 - 0
lib/twig/AUTHORS

@@ -0,0 +1,9 @@
+Twig is written and maintained by the Twig Team:
+
+Lead Developer:
+
+- Fabien Potencier <fabien.potencier@symfony-project.org>
+
+Project Founder:
+
+- Armin Ronacher <armin.ronacher@active-4.com>

+ 474 - 0
lib/twig/CHANGELOG

@@ -0,0 +1,474 @@
+* 1.6.4 (2012-04-02)
+
+ * fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3
+ * fixed performance when compiling large files
+ * optimized parent template creation when the template does not use dynamic inheritance
+
+* 1.6.3 (2012-03-22)
+
+ * fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension
+ * fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot
+ * made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate
+
+* 1.6.2 (2012-03-18)
+
+ * fixed sandbox mode when used with inheritance
+ * added preserveKeys support for the slice filter
+ * fixed the date filter when a DateTime instance is passed with a specific timezone
+ * added a trim filter
+
+* 1.6.1 (2012-02-29)
+
+ * fixed Twig C extension
+ * removed the creation of Twig_Markup instances when not needed
+ * added a way to set the default global timezone for dates
+ * fixed the slice filter on strings when the length is not specified
+ * fixed the creation of the cache directory in case of a race condition
+
+* 1.6.0 (2012-02-04)
+
+ * fixed raw blocks when used with the whitespace trim option
+ * made a speed optimization to macro calls when imported via the "from" tag
+ * fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added
+ * fixed the attribute function when passing arguments
+ * added slice notation support for the [] operator (syntactic sugar for the slice operator)
+ * added a slice filter
+ * added string support for the reverse filter
+ * fixed the empty test and the length filter for Twig_Markup instances
+ * added a date function to ease date comparison
+ * fixed unary operators precedence
+ * added recursive parsing support in the parser
+ * added string and integer handling for the random function
+
+* 1.5.1 (2012-01-05)
+
+ * fixed a regression when parsing strings
+
+* 1.5.0 (2012-01-04)
+
+ * added Traversable objects support for the join filter
+
+* 1.5.0-RC2 (2011-12-30)
+
+ * added a way to set the default global date interval format
+ * fixed the date filter for DateInterval instances (setTimezone() does not exist for them)
+ * refactored Twig_Template::display() to ease its extension
+ * added a number_format filter
+
+* 1.5.0-RC1 (2011-12-26)
+
+ * removed the need to quote hash keys
+ * allowed hash keys to be any expression
+ * added a do tag
+ * added a flush tag
+ * added support for dynamically named filters and functions
+ * added a dump function to help debugging templates
+ * added a nl2br filter
+ * added a random function
+ * added a way to change the default format for the date filter
+ * fixed the lexer when an operator ending with a letter ends a line
+ * added string interpolation support
+ * enhanced exceptions for unknown filters, functions, tests, and tags
+
+* 1.4.0 (2011-12-07)
+
+ * fixed lexer when using big numbers (> PHP_INT_MAX)
+ * added missing preserveKeys argument to the reverse filter
+ * fixed macros containing filter tag calls
+
+* 1.4.0-RC2 (2011-11-27)
+
+ * removed usage of Reflection in Twig_Template::getAttribute()
+ * added a C extension that can optionally replace Twig_Template::getAttribute()
+ * added negative timestamp support to the date filter
+
+* 1.4.0-RC1 (2011-11-20)
+
+ * optimized variable access when using PHP 5.4
+ * changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby
+ * added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders
+ * added Twig_Function_Node to allow more complex functions to have their own Node class
+ * added Twig_Filter_Node to allow more complex filters to have their own Node class
+ * added Twig_Test_Node to allow more complex tests to have their own Node class
+ * added a better error message when a template is empty but contain a BOM
+ * fixed "in" operator for empty strings
+ * fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option)
+ * changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order)
+ * added Twig_Environment::display()
+ * made the escape filter smarter when the encoding is not supported by PHP
+ * added a convert_encoding filter
+ * moved all node manipulations outside the compile() Node method
+ * made several speed optimizations
+
+* 1.3.0 (2011-10-08)
+
+no changes
+
+* 1.3.0-RC1 (2011-10-04)
+
+ * added an optimization for the parent() function
+ * added cache reloading when auto_reload is true and an extension has been modified
+ * added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup)
+ * allowed empty templates to be used as traits
+ * added traits support for the "parent" function
+
+* 1.2.0 (2011-09-13)
+
+no changes
+
+* 1.2.0-RC1 (2011-09-10)
+
+ * enhanced the exception when a tag remains unclosed
+ * added support for empty Countable objects for the "empty" test
+ * fixed algorithm that determines if a template using inheritance is valid (no output between block definitions)
+ * added better support for encoding problems when escaping a string (available as of PHP 5.4)
+ * added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %})
+ * added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %})
+ * added support for bitwise operators in expressions
+ * added the "attribute" function to allow getting dynamic attributes on variables
+ * added Twig_Loader_Chain
+ * added Twig_Loader_Array::setTemplate()
+ * added an optimization for the set tag when used to capture a large chunk of static text
+ * changed name regex to match PHP one "[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" (works for blocks, tags, functions, filters, and macros)
+ * removed the possibility to use the "extends" tag from a block
+ * added "if" modifier support to "for" loops
+
+* 1.1.2 (2011-07-30)
+
+ * fixed json_encode filter on PHP 5.2
+ * fixed regression introduced in 1.1.1 ({{ block(foo|lower) }})
+ * fixed inheritance when using conditional parents
+ * fixed compilation of templates when the body of a child template is not empty
+ * fixed output when a macro throws an exception
+ * fixed a parsing problem when a large chunk of text is enclosed in a comment tag
+ * added PHPDoc for all Token parsers and Core extension functions
+
+* 1.1.1 (2011-07-17)
+
+ * added a performance optimization in the Optimizer (also helps to lower the number of nested level calls)
+ * made some performance improvement for some edge cases
+
+* 1.1.0 (2011-06-28)
+
+ * fixed json_encode filter
+
+* 1.1.0-RC3 (2011-06-24)
+
+ * fixed method case-sensitivity when using the sandbox mode
+ * added timezone support for the date filter
+ * fixed possible security problems with NUL bytes
+
+* 1.1.0-RC2 (2011-06-16)
+
+ * added an exception when the template passed to "use" is not a string
+ * made 'a.b is defined' not throw an exception if a is not defined (in strict mode)
+ * added {% line \d+ %} directive
+
+* 1.1.0-RC1 (2011-05-28)
+
+Flush your cache after upgrading.
+
+ * fixed date filter when using a timestamp
+ * fixed the defined test for some cases
+ * fixed a parsing problem when a large chunk of text is enclosed in a raw tag
+ * added support for horizontal reuse of template blocks (see docs for more information)
+ * added whitespace control modifier to all tags (see docs for more information)
+ * added null as an alias for none (the null test is also an alias for the none test now)
+ * made TRUE, FALSE, NONE equivalent to their lowercase counterparts
+ * wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line
+ * moved display() method to Twig_Template (generated templates should now use doDisplay() instead)
+
+* 1.0.0 (2011-03-27)
+
+ * fixed output when using mbstring
+ * fixed duplicate call of methods when using the sandbox
+ * made the charset configurable for the escape filter
+
+* 1.0.0-RC2 (2011-02-21)
+
+ * changed the way {% set %} works when capturing (the content is now marked as safe)
+ * added support for macro name in the endmacro tag
+ * make Twig_Error compatible with PHP 5.3.0 >
+ * fixed an infinite loop on some Windows configurations
+ * fixed the "length" filter for numbers
+ * fixed Template::getAttribute() as properties in PHP are case sensitive
+ * removed coupling between Twig_Node and Twig_Template
+ * fixed the ternary operator precedence rule
+
+* 1.0.0-RC1 (2011-01-09)
+
+Backward incompatibilities:
+
+ * the "items" filter, which has been deprecated for quite a long time now, has been removed
+ * the "range" filter has been converted to a function: 0|range(10) -> range(0, 10)
+ * the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }}
+ * the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }}
+ * the "for" tag does not support "joined by" anymore
+ * the "autoescape" first argument is now "true"/"false" (instead of "on"/"off")
+ * the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %})
+ * the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %})
+ * removed the grammar and simple token parser (moved to the Twig Extensions repository)
+
+Changes:
+
+ * added "needs_context" option for filters and functions (the context is then passed as a first argument)
+ * added global variables support
+ * made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode)
+ * added the "from" tag to import macros as functions
+ * added support for functions (a function is just syntactic sugar for a getAttribute() call)
+ * made macros callable when sandbox mode is enabled
+ * added an exception when a macro uses a reserved name
+ * the "default" filter now uses the "empty" test instead of just checking for null
+ * added the "empty" test
+
+* 0.9.10 (2010-12-16)
+
+Backward incompatibilities:
+
+ * The Escaper extension is enabled by default, which means that all displayed
+   variables are now automatically escaped. You can revert to the previous
+   behavior by removing the extension via $env->removeExtension('escaper')
+   or just set the 'autoescape' option to 'false'.
+ * removed the "without loop" attribute for the "for" tag (not needed anymore
+   as the Optimizer take care of that for most cases)
+ * arrays and hashes have now a different syntax
+     * arrays keep the same syntax with square brackets: [1, 2]
+     * hashes now use curly braces (["a": "b"] should now be written as {"a": "b"})
+     * support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1})
+ * the i18n extension is now part of the Twig Extensions repository
+
+Changes:
+
+ * added the merge filter
+ * removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead
+ * fixed usage of operators as method names (like is, in, and not)
+ * changed the order of execution for node visitors
+ * fixed default() filter behavior when used with strict_variables set to on
+ * fixed filesystem loader compatibility with PHAR files
+ * enhanced error messages when an unexpected token is parsed in an expression
+ * fixed filename not being added to syntax error messages
+ * added the autoescape option to enable/disable autoescaping
+ * removed the newline after a comment (mimicks PHP behavior)
+ * added a syntax error exception when parent block is used on a template that does not extend another one
+ * made the Escaper extension enabled by default
+ * fixed sandbox extension when used with auto output escaping
+ * fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved)
+ * added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters)
+ * added priority to node visitors
+
+* 0.9.9 (2010-11-28)
+
+Backward incompatibilities:
+ * the self special variable has been renamed to _self
+ * the odd and even filters are now tests:
+     {{ foo|odd }} must now be written {{ foo is odd }}
+ * the "safe" filter has been renamed to "raw"
+ * in Node classes,
+        sub-nodes are now accessed via getNode() (instead of property access)
+        attributes via getAttribute() (instead of array access)
+ * the urlencode filter had been renamed to url_encode
+ * the include tag now merges the passed variables with the current context by default
+   (the old behavior is still possible by adding the "only" keyword)
+ * moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime)
+ * removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead)
+ * the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }})
+
+Changes:
+ * added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template
+ * changed trans tag to accept any variable for the plural count
+ * fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements)
+ * added the ** (power) operator
+ * changed the algorithm used for parsing expressions
+ * added the spaceless tag
+ * removed trim_blocks option
+ * added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar())
+ * changed all exceptions to extend Twig_Error
+ * fixed unary expressions ({{ not(1 or 0) }})
+ * fixed child templates (with an extend tag) that uses one or more imports
+ * added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }})
+ * escaping has been rewritten
+ * the implementation of template inheritance has been rewritten
+   (blocks can now be called individually and still work with inheritance)
+ * fixed error handling for if tag when a syntax error occurs within a subparse process
+ * added a way to implement custom logic for resolving token parsers given a tag name
+ * fixed js escaper to be stricter (now uses a whilelist-based js escaper)
+ * added the following filers: "constant", "trans", "replace", "json_encode"
+ * added a "constant" test
+ * fixed objects with __toString() not being autoescaped
+ * fixed subscript expressions when calling __call() (methods now keep the case)
+ * added "test" feature (accessible via the "is" operator)
+ * removed the debug tag (should be done in an extension)
+ * fixed trans tag when no vars are used in plural form
+ * fixed race condition when writing template cache
+ * added the special _charset variable to reference the current charset
+ * added the special _context variable to reference the current context
+ * renamed self to _self (to avoid conflict)
+ * fixed Twig_Template::getAttribute() for protected properties
+
+* 0.9.8 (2010-06-28)
+
+Backward incompatibilities:
+ * the trans tag plural count is now attached to the plural tag:
+    old: `{% trans count %}...{% plural %}...{% endtrans %}`
+    new: `{% trans %}...{% plural count %}...{% endtrans %}`
+
+ * added a way to translate strings coming from a variable ({% trans var %})
+ * fixed trans tag when used with the Escaper extension
+ * fixed default cache umask
+ * removed Twig_Template instances from the debug tag output
+ * fixed objects with __isset() defined
+ * fixed set tag when used with a capture
+ * fixed type hinting for Twig_Environment::addFilter() method
+
+* 0.9.7 (2010-06-12)
+
+Backward incompatibilities:
+ * changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %})
+ * removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
+ * refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
+
+ * added self as a special variable that refers to the current template (useful for importing macros from the current template)
+ * added Twig_Template instance support to the include tag
+ * added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
+ * added a grammar sub-framework to ease the creation of custom tags
+ * fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface)
+ * removed the Twig_Resource::resolveMissingFilter() method
+ * fixed the filter tag which did not apply filtering to included files
+ * added a bunch of unit tests
+ * added a bunch of phpdoc
+ * added a sandbox tag in the sandbox extension
+ * changed the date filter to support any date format supported by DateTime
+ * added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default)
+ * added the lexer, parser, and compiler as arguments to the Twig_Environment constructor
+ * changed the cache option to only accepts an explicit path to a cache directory or false
+ * added a way to add token parsers, filters, and visitors without creating an extension
+ * added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
+ * changed the generated code to match the new coding standards
+ * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
+ * added an exception when a child template has a non-empty body (as it is always ignored when rendering)
+
+* 0.9.6 (2010-05-12)
+
+ * fixed variables defined outside a loop and for which the value changes in a for loop
+ * fixed the test suite for PHP 5.2 and older versions of PHPUnit
+ * added support for __call() in expression resolution
+ * fixed node visiting for macros (macros are now visited by visitors as any other node)
+ * fixed nested block definitions with a parent call (rarely useful but nonetheless supported now)
+ * added the cycle filter
+ * fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII
+ * added a long-syntax for the set tag ({% set foo %}...{% endset %})
+ * unit tests are now powered by PHPUnit
+ * added support for gettext via the `i18n` extension
+ * fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values
+ * added a more useful exception if an if tag is not closed properly
+ * added support for escaping strategy in the autoescape tag
+ * fixed lexer when a template has a big chunk of text between/in a block
+
+* 0.9.5 (2010-01-20)
+
+As for any new release, don't forget to remove all cached templates after
+upgrading.
+
+If you have defined custom filters, you MUST upgrade them for this release. To
+upgrade, replace "array" with "new Twig_Filter_Function", and replace the
+environment constant by the "needs_environment" option:
+
+  // before
+  'even'   => array('twig_is_even_filter', false),
+  'escape' => array('twig_escape_filter', true),
+
+  // after
+  'even'   => new Twig_Filter_Function('twig_is_even_filter'),
+  'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
+
+If you have created NodeTransformer classes, you will need to upgrade them to
+the new interface (please note that the interface is not yet considered
+stable).
+
+ * fixed list nodes that did not extend the Twig_NodeListInterface
+ * added the "without loop" option to the for tag (it disables the generation of the loop variable)
+ * refactored node transformers to node visitors
+ * fixed automatic-escaping for blocks
+ * added a way to specify variables to pass to an included template
+ * changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
+ * improved the filter system to allow object methods to be used as filters
+ * changed the Array and String loaders to actually make use of the cache mechanism
+ * included the default filter function definitions in the extension class files directly (Core, Escaper)
+ * added the // operator (like the floor() PHP function)
+ * added the .. operator (as a syntactic sugar for the range filter when the step is 1)
+ * added the in operator (as a syntactic sugar for the in filter)
+ * added the following filters in the Core extension: in, range
+ * added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
+ * enhanced some error messages to provide better feedback in case of parsing errors
+
+* 0.9.4 (2009-12-02)
+
+If you have custom loaders, you MUST upgrade them for this release: The
+Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
+been changed (see the source code for more information or the documentation).
+
+ * added support for DateTime instances for the date filter
+ * fixed loop.last when the array only has one item
+ * made it possible to insert newlines in tag and variable blocks
+ * fixed a bug when a literal '\n' were present in a template text
+ * fixed bug when the filename of a template contains */
+ * refactored loaders
+
+* 0.9.3 (2009-11-11)
+
+This release is NOT backward compatible with the previous releases.
+
+  The loaders do not take the cache and autoReload arguments anymore. Instead,
+  the Twig_Environment class has two new options: cache and auto_reload.
+  Upgrading your code means changing this kind of code:
+
+      $loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true);
+      $twig = new Twig_Environment($loader);
+
+  to something like this:
+
+      $loader = new Twig_Loader_Filesystem('/path/to/templates');
+      $twig = new Twig_Environment($loader, array(
+        'cache' => '/path/to/compilation_cache',
+        'auto_reload' => true,
+      ));
+
+ * deprecated the "items" filter as it is not needed anymore
+ * made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader
+ * optimized template loading speed
+ * removed output when an error occurs in a template and render() is used
+ * made major speed improvements for loops (up to 300% on even the smallest loops)
+ * added properties as part of the sandbox mode
+ * added public properties support (obj.item can now be the item property on the obj object)
+ * extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} )
+ * fixed bug when \ was used in HTML
+
+* 0.9.2 (2009-10-29)
+
+ * made some speed optimizations
+ * changed the cache extension to .php
+ * added a js escaping strategy
+ * added support for short block tag
+ * changed the filter tag to allow chained filters
+ * made lexer more flexible as you can now change the default delimiters
+ * added set tag
+ * changed default directory permission when cache dir does not exist (more secure)
+ * added macro support
+ * changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance
+ * made Twig_Autoloader::autoload() a static method
+ * avoid writing template file if an error occurs
+ * added $ escaping when outputting raw strings
+ * enhanced some error messages to ease debugging
+ * fixed empty cache files when the template contains an error
+
+* 0.9.1 (2009-10-14)
+
+  * fixed a bug in PHP 5.2.6
+  * fixed numbers with one than one decimal
+  * added support for method calls with arguments ({{ foo.bar('a', 43) }})
+  * made small speed optimizations
+  * made minor tweaks to allow better extensibility and flexibility
+
+* 0.9.0 (2009-10-12)
+
+ * Initial release

+ 31 - 0
lib/twig/LICENSE

@@ -0,0 +1,31 @@
+Copyright (c) 2009 by the Twig Team, see AUTHORS for more details.
+
+Some rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of the contributors may not be used to endorse or
+      promote products derived from this software without specific
+      prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 15 - 0
lib/twig/README.markdown

@@ -0,0 +1,15 @@
+Twig, the flexible, fast, and secure template language for PHP
+==============================================================
+
+Twig is a template language for PHP, released under the new BSD license (code
+and documentation).
+
+Twig uses a syntax similar to the Django and Jinja template languages which
+inspired the Twig runtime environment.
+
+More Information
+----------------
+
+Read the [documentation][1] for more information.
+
+[1]: http://twig.sensiolabs.org/documentation

+ 42 - 0
lib/twig/bin/create_pear_package.php

@@ -0,0 +1,42 @@
+<?php
+
+if (!isset($argv[1]))
+{
+    die('You must provide the version (1.0.0)');
+}
+
+if (!isset($argv[2]))
+{
+    die('You must provide the stability (alpha, beta, or stable)');
+}
+
+$context = array(
+    'date'          => date('Y-m-d'),
+    'time'          => date('H:m:00'),
+    'version'       => $argv[1],
+    'api_version'   => $argv[1],
+    'stability'     => $argv[2],
+    'api_stability' => $argv[2],
+);
+
+$context['files'] = '';
+$path = realpath(dirname(__FILE__).'/../lib/Twig');
+foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY) as $file)
+{
+    if (preg_match('/\.php$/', $file))
+    {
+        $name = str_replace($path.'/', '', $file);
+        $context['files'] .= '        <file install-as="Twig/'.$name.'" name="'.$name.'" role="php" />'."\n";
+    }
+}
+
+$template = file_get_contents(dirname(__FILE__).'/../package.xml.tpl');
+$content = preg_replace_callback('/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/', 'replace_parameters', $template);
+file_put_contents(dirname(__FILE__).'/../package.xml', $content);
+
+function replace_parameters($matches)
+{
+    global $context;
+
+    return isset($context[$matches[1]]) ? $context[$matches[1]] : null;
+}

+ 27 - 0
lib/twig/composer.json

@@ -0,0 +1,27 @@
+{
+    "name": "twig/twig",
+    "type": "library",
+    "description": "Twig, the flexible, fast, and secure template language for PHP",
+    "keywords": ["templating"],
+    "homepage": "http://twig.sensiolabs.org",
+    "version": "1.6.4",
+    "license": "BSD",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Armin Ronacher",
+            "email": "armin.ronacher@active-4.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.2.4"
+    },
+    "autoload": {
+        "psr-0" : {
+            "Twig_" : "lib/"
+        }
+    }
+}

+ 489 - 0
lib/twig/doc/advanced.rst

@@ -0,0 +1,489 @@
+Extending Twig
+==============
+
+Twig can be extended in many ways; you can add extra tags, filters, tests,
+operators, global variables, and functions. You can even extend the parser
+itself with node visitors.
+
+.. note::
+
+    This chapter describes how to extend Twig easily. If you want to reuse
+    your changes in different projects or if you want to share them with
+    others, you should then create an extension as described in the next
+    chapter.
+
+Before extending Twig, you must understand the differences between all the
+different possible extension points and when to use them.
+
+First, remember that Twig has two main language constructs:
+
+* ``{{ }}``: used to print the result of an expression evaluation;
+
+* ``{% %}``: used to execute statements.
+
+To understand why Twig exposes so many extension points, let's see how to
+implement a *Lorem ipsum* generator (it needs to know the number of words to
+generate).
+
+You can use a ``lipsum`` *tag*:
+
+.. code-block:: jinja
+
+    {% lipsum 40 %}
+
+That works, but using a tag for ``lipsum`` is not a good idea for at least
+three main reasons:
+
+* ``lipsum`` is not a language construct;
+* The tag outputs something;
+* The tag is not flexible as you cannot use it in an expression:
+
+  .. code-block:: jinja
+
+      {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
+
+In fact, you rarely need to create tags; and that's good news because tags are
+the most complex extension point of Twig.
+
+Now, let's use a ``lipsum`` *filter*:
+
+.. code-block:: jinja
+
+    {{ 40|lipsum }}
+
+Again, it works, but it looks weird. A filter transforms the passed value to
+something else but here we use the value to indicate the number of words to
+generate.
+
+Next, let's use a ``lipsum`` *function*:
+
+.. code-block:: jinja
+
+    {{ lipsum(40) }}
+
+Here we go. For this specific example, the creation of a function is the
+extension point to use. And you can use it anywhere an expression is accepted:
+
+.. code-block:: jinja
+
+    {{ 'some text' ~ ipsum(40) ~ 'some more text' }}
+
+    {% set ipsum = ipsum(40) %}
+
+Last but not the least, you can also use a *global* object with a method able
+to generate lorem ipsum text:
+
+.. code-block:: jinja
+
+    {{ text.lipsum(40) }}
+
+As a rule of thumb, use functions for frequently used features and global
+objects for everything else.
+
+Keep in mind the following when you want to extend Twig:
+
+========== ========================== ========== =========================
+What?      Implementation difficulty? How often? When?
+========== ========================== ========== =========================
+*macro*    trivial                    frequent   Content generation
+*global*   trivial                    frequent   Helper object
+*function* trivial                    frequent   Content generation
+*filter*   trivial                    frequent   Value transformation
+*tag*      complex                    rare       DSL language construct
+*test*     trivial                    rare       Boolean decision
+*operator* trivial                    rare       Values transformation
+========== ========================== ========== =========================
+
+Globals
+-------
+
+A global variable is like any other template variable, except that it's
+available in all templates and macros::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addGlobal('text', new Text());
+
+You can then use the ``text`` variable anywhere in a template:
+
+.. code-block:: jinja
+
+    {{ text.lipsum(40) }}
+
+Filters
+-------
+
+A filter is a regular PHP function or an object method that takes the left
+side of the filter (before the pipe ``|``) as first argument and the extra
+arguments passed to the filter (within parentheses ``()``) as extra arguments.
+
+Defining a filter is as easy as associating the filter name with a PHP
+callable. For instance, let's say you have the following code in a template:
+
+.. code-block:: jinja
+
+    {{ 'TWIG'|lower }}
+
+When compiling this template to PHP, Twig looks for the PHP callable
+associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig
+filter, and it is simply mapped to the PHP ``strtolower()`` function. After
+compilation, the generated PHP code is roughly equivalent to:
+
+.. code-block:: html+php
+
+    <?php echo strtolower('TWIG') ?>
+
+As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP
+function.
+
+A filter can also take extra arguments like in the following example:
+
+.. code-block:: jinja
+
+    {{ now|date('d/m/Y') }}
+
+In this case, the extra arguments are passed to the function after the main
+argument, and the compiled code is equivalent to:
+
+.. code-block:: html+php
+
+    <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
+
+Let's see how to create a new filter.
+
+In this section, we will create a ``rot13`` filter, which should return the
+`rot13`_ transformation of a string. Here is an example of its usage and the
+expected output:
+
+.. code-block:: jinja
+
+    {{ "Twig"|rot13 }}
+
+    {# should displays Gjvt #}
+
+Adding a filter is as simple as calling the ``addFilter()`` method on the
+``Twig_Environment`` instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addFilter('rot13', new Twig_Filter_Function('str_rot13'));
+
+The second argument of ``addFilter()`` is an instance of ``Twig_Filter``.
+Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The
+first argument passed to the ``Twig_Filter_Function`` constructor is the name
+of the PHP function to call, here ``str_rot13``, a native PHP function.
+
+Let's say I now want to be able to add a prefix before the converted string:
+
+.. code-block:: jinja
+
+    {{ "Twig"|rot13('prefix_') }}
+
+    {# should displays prefix_Gjvt #}
+
+As the PHP ``str_rot13()`` function does not support this requirement, let's
+create a new PHP function::
+
+    function project_compute_rot13($string, $prefix = '')
+    {
+        return $prefix.str_rot13($string);
+    }
+
+As you can see, the ``prefix`` argument of the filter is passed as an extra
+argument to the ``project_compute_rot13()`` function.
+
+Adding this filter is as easy as before::
+
+    $twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13'));
+
+For better encapsulation, a filter can also be defined as a static method of a
+class. The ``Twig_Filter_Function`` class can also be used to register such
+static methods as filters::
+
+    $twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter'));
+
+.. tip::
+
+    In an extension, you can also define a filter as a static method of the
+    extension class.
+
+Environment aware Filters
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``Twig_Filter`` classes take options as their last argument. For instance,
+if you want access to the current environment instance in your filter, set the
+``needs_environment`` option to ``true``::
+
+    $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
+
+Twig will then pass the current environment as the first argument to the
+filter call::
+
+    function twig_compute_rot13(Twig_Environment $env, $string)
+    {
+        // get the current charset for instance
+        $charset = $env->getCharset();
+
+        return str_rot13($string);
+    }
+
+Automatic Escaping
+~~~~~~~~~~~~~~~~~~
+
+If automatic escaping is enabled, the output of the filter may be escaped
+before printing. If your filter acts as an escaper (or explicitly outputs html
+or javascript code), you will want the raw output to be printed. In such a
+case, set the ``is_safe`` option::
+
+    $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
+
+Some filters may have to work on already escaped or safe values. In such a
+case, set the ``pre_escape`` option::
+
+    $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
+
+Dynamic Filters
+~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.5
+    Dynamic filters support was added in Twig 1.5.
+
+A filter name containing the special ``*`` character is a dynamic filter as
+the ``*`` can be any string::
+
+    $twig->addFilter('*_path', new Twig_Filter_Function('twig_path'));
+
+    function twig_path($name, $arguments)
+    {
+        // ...
+    }
+
+The following filters will be matched by the above defined dynamic filter:
+
+* ``product_path``
+* ``category_path``
+
+A dynamic filter can define more than one dynamic parts::
+
+    $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
+
+    function twig_path($name, $suffix, $arguments)
+    {
+        // ...
+    }
+
+The filter will receive all dynamic part values before the normal filters
+arguments. For instance, a call to ``'foo'|a_path_b()`` will result in the
+following PHP call: ``twig_path('a', 'b', 'foo')``.
+
+Functions
+---------
+
+A function is a regular PHP function or an object method that can be called from
+templates.
+
+.. code-block:: jinja
+
+    {{ constant("DATE_W3C") }}
+
+When compiling this template to PHP, Twig looks for the PHP callable
+associated with the ``constant`` function. The ``constant`` function is a built-in Twig
+function, and it is simply mapped to the PHP ``constant()`` function. After
+compilation, the generated PHP code is roughly equivalent to:
+
+.. code-block:: html+php
+
+    <?php echo constant('DATE_W3C') ?>
+
+Adding a function is similar to adding a filter. This can be done by calling the
+``addFunction()`` method on the ``Twig_Environment`` instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addFunction('functionName', new Twig_Function_Function('someFunction'));
+
+You can also expose extension methods as functions in your templates::
+
+    // $this is an object that implements Twig_ExtensionInterface.
+    $twig = new Twig_Environment($loader);
+    $twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));
+
+Functions also support ``needs_environment`` and ``is_safe`` parameters.
+
+Dynamic Functions
+~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.5
+    Dynamic functions support was added in Twig 1.5.
+
+A function name containing the special ``*`` character is a dynamic function
+as the ``*`` can be any string::
+
+    $twig->addFunction('*_path', new Twig_Function_Function('twig_path'));
+
+    function twig_path($name, $arguments)
+    {
+        // ...
+    }
+
+The following functions will be matched by the above defined dynamic function:
+
+* ``product_path``
+* ``category_path``
+
+A dynamic function can define more than one dynamic parts::
+
+    $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
+
+    function twig_path($name, $suffix, $arguments)
+    {
+        // ...
+    }
+
+The function will receive all dynamic part values before the normal functions
+arguments. For instance, a call to ``a_path_b('foo')`` will result in the
+following PHP call: ``twig_path('a', 'b', 'foo')``.
+
+Tags
+----
+
+One of the most exciting feature of a template engine like Twig is the
+possibility to define new language constructs. This is also the most complex
+feature as you need to understand how Twig's internals work.
+
+Let's create a simple ``set`` tag that allows the definition of simple
+variables from within a template. The tag can be used like follows:
+
+.. code-block:: jinja
+
+    {% set name = "value" %}
+
+    {{ name }}
+
+    {# should output value #}
+
+.. note::
+
+    The ``set`` tag is part of the Core extension and as such is always
+    available. The built-in version is slightly more powerful and supports
+    multiple assignments by default (cf. the template designers chapter for
+    more information).
+
+Three steps are needed to define a new tag:
+
+* Defining a Token Parser class (responsible for parsing the template code);
+
+* Defining a Node class (responsible for converting the parsed code to PHP);
+
+* Registering the tag.
+
+Registering a new tag
+~~~~~~~~~~~~~~~~~~~~~
+
+Adding a tag is as simple as calling the ``addTokenParser`` method on the
+``Twig_Environment`` instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addTokenParser(new Project_Set_TokenParser());
+
+Defining a Token Parser
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Now, let's see the actual code of this class::
+
+    class Project_Set_TokenParser extends Twig_TokenParser
+    {
+        public function parse(Twig_Token $token)
+        {
+            $lineno = $token->getLine();
+            $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
+            $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
+            $value = $this->parser->getExpressionParser()->parseExpression();
+
+            $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
+
+            return new Project_Set_Node($name, $value, $lineno, $this->getTag());
+        }
+
+        public function getTag()
+        {
+            return 'set';
+        }
+    }
+
+The ``getTag()`` method must return the tag we want to parse, here ``set``.
+
+The ``parse()`` method is invoked whenever the parser encounters a ``set``
+tag. It should return a ``Twig_Node`` instance that represents the node (the
+``Project_Set_Node`` calls creating is explained in the next section).
+
+The parsing process is simplified thanks to a bunch of methods you can call
+from the token stream (``$this->parser->getStream()``):
+
+* ``getCurrent()``: Gets the current token in the stream.
+
+* ``next()``: Moves to the next token in the stream, *but returns the old one*.
+
+* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
+  the current token is of a particular type or value (or both). The value may be an
+  array of several possible values.
+
+* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
+  type/value a syntax error is thrown. Otherwise, if the type and value are correct,
+  the token is returned and the stream moves to the next token.
+
+* ``look()``: Looks a the next token without consuming it.
+
+Parsing expressions is done by calling the ``parseExpression()`` like we did for
+the ``set`` tag.
+
+.. tip::
+
+    Reading the existing ``TokenParser`` classes is the best way to learn all
+    the nitty-gritty details of the parsing process.
+
+Defining a Node
+~~~~~~~~~~~~~~~
+
+The ``Project_Set_Node`` class itself is rather simple::
+
+    class Project_Set_Node extends Twig_Node
+    {
+        public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null)
+        {
+            parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag);
+        }
+
+        public function compile(Twig_Compiler $compiler)
+        {
+            $compiler
+                ->addDebugInfo($this)
+                ->write('$context[\''.$this->getAttribute('name').'\'] = ')
+                ->subcompile($this->getNode('value'))
+                ->raw(";\n")
+            ;
+        }
+    }
+
+The compiler implements a fluid interface and provides methods that helps the
+developer generate beautiful and readable PHP code:
+
+* ``subcompile()``: Compiles a node.
+
+* ``raw()``: Writes the given string as is.
+
+* ``write()``: Writes the given string by adding indentation at the beginning
+  of each line.
+
+* ``string()``: Writes a quoted string.
+
+* ``repr()``: Writes a PHP representation of a given value (see
+  ``Twig_Node_For`` for a usage example).
+
+* ``addDebugInfo()``: Adds the line of the original template file related to
+  the current node as a comment.
+
+* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
+  usage example).
+
+* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
+  usage example).
+
+.. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php

+ 479 - 0
lib/twig/doc/api.rst

@@ -0,0 +1,479 @@
+Twig for Developers
+===================
+
+This chapter describes the API to Twig and not the template language. It will
+be most useful as reference to those implementing the template interface to
+the application and not those who are creating Twig templates.
+
+Basics
+------
+
+Twig uses a central object called the **environment** (of class
+``Twig_Environment``). Instances of this class are used to store the
+configuration and extensions, and are used to load templates from the file
+system or other locations.
+
+Most applications will create one ``Twig_Environment`` object on application
+initialization and use that to load templates. In some cases it's however
+useful to have multiple environments side by side, if different configurations
+are in use.
+
+The simplest way to configure Twig to load templates for your application
+looks roughly like this::
+
+    require_once '/path/to/lib/Twig/Autoloader.php';
+    Twig_Autoloader::register();
+
+    $loader = new Twig_Loader_Filesystem('/path/to/templates');
+    $twig = new Twig_Environment($loader, array(
+        'cache' => '/path/to/compilation_cache',
+    ));
+
+This will create a template environment with the default settings and a loader
+that looks up the templates in the ``/path/to/templates/`` folder. Different
+loaders are available and you can also write your own if you want to load
+templates from a database or other resources.
+
+.. note::
+
+    Notice that the second argument of the environment is an array of options.
+    The ``cache`` option is a compilation cache directory, where Twig caches
+    the compiled templates to avoid the parsing phase for sub-sequent
+    requests. It is very different from the cache you might want to add for
+    the evaluated templates. For such a need, you can use any available PHP
+    cache library.
+
+To load a template from this environment you just have to call the
+``loadTemplate()`` method which then returns a ``Twig_Template`` instance::
+
+    $template = $twig->loadTemplate('index.html');
+
+To render the template with some variables, call the ``render()`` method::
+
+    echo $template->render(array('the' => 'variables', 'go' => 'here'));
+
+.. note::
+
+    The ``display()`` method is a shortcut to output the template directly.
+
+You can also load and render the template in one fell swoop::
+
+    echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
+
+Environment Options
+-------------------
+
+When creating a new ``Twig_Environment`` instance, you can pass an array of
+options as the constructor second argument::
+
+    $twig = new Twig_Environment($loader, array('debug' => true));
+
+The following options are available:
+
+* ``debug``: When set to ``true``, the generated templates have a
+  ``__toString()`` method that you can use to display the generated nodes
+  (default to ``false``).
+
+* ``charset``: The charset used by the templates (default to ``utf-8``).
+
+* ``base_template_class``: The base template class to use for generated
+  templates (default to ``Twig_Template``).
+
+* ``cache``: An absolute path where to store the compiled templates, or
+  ``false`` to disable caching (which is the default).
+
+* ``auto_reload``: When developing with Twig, it's useful to recompile the
+  template whenever the source code changes. If you don't provide a value for
+  the ``auto_reload`` option, it will be determined automatically based on the
+  ``debug`` value.
+
+* ``strict_variables``: If set to ``false``, Twig will silently ignore invalid
+  variables (variables and or attributes/methods that do not exist) and
+  replace them with a ``null`` value. When set to ``true``, Twig throws an
+  exception instead (default to ``false``).
+
+* ``autoescape``: If set to ``true``, auto-escaping will be enabled by default
+  for all templates (default to ``true``).
+
+* ``optimizations``: A flag that indicates which optimizations to apply
+  (default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
+  disable).
+
+Loaders
+-------
+
+Loaders are responsible for loading templates from a resource such as the file
+system.
+
+Compilation Cache
+~~~~~~~~~~~~~~~~~
+
+All template loaders can cache the compiled templates on the filesystem for
+future reuse. It speeds up Twig a lot as templates are only compiled once; and
+the performance boost is even larger if you use a PHP accelerator such as APC.
+See the ``cache`` and ``auto_reload`` options of ``Twig_Environment`` above
+for more information.
+
+Built-in Loaders
+~~~~~~~~~~~~~~~~
+
+Here is a list of the built-in loaders Twig provides:
+
+* ``Twig_Loader_Filesystem``: Loads templates from the file system. This
+  loader can find templates in folders on the file system and is the preferred
+  way to load them::
+
+        $loader = new Twig_Loader_Filesystem($templateDir);
+
+  It can also look for templates in an array of directories::
+
+        $loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2));
+
+  With such a configuration, Twig will first look for templates in
+  ``$templateDir1`` and if they do not exist, it will fallback to look for
+  them in the ``$templateDir2``.
+
+* ``Twig_Loader_String``: Loads templates from a string. It's a dummy loader
+  as you pass it the source code directly::
+
+        $loader = new Twig_Loader_String();
+
+* ``Twig_Loader_Array``: Loads a template from a PHP array. It's passed an
+  array of strings bound to template names. This loader is useful for unit
+  testing::
+
+        $loader = new Twig_Loader_Array($templates);
+
+.. tip::
+
+    When using the ``Array`` or ``String`` loaders with a cache mechanism, you
+    should know that a new cache key is generated each time a template content
+    "changes" (the cache key being the source code of the template). If you
+    don't want to see your cache grows out of control, you need to take care
+    of clearing the old cache file by yourself.
+
+Create your own Loader
+~~~~~~~~~~~~~~~~~~~~~~
+
+All loaders implement the ``Twig_LoaderInterface``::
+
+    interface Twig_LoaderInterface
+    {
+        /**
+         * Gets the source code of a template, given its name.
+         *
+         * @param  string $name string The name of the template to load
+         *
+         * @return string The template source code
+         */
+        function getSource($name);
+
+        /**
+         * Gets the cache key to use for the cache for a given template name.
+         *
+         * @param  string $name string The name of the template to load
+         *
+         * @return string The cache key
+         */
+        function getCacheKey($name);
+
+        /**
+         * Returns true if the template is still fresh.
+         *
+         * @param string    $name The template name
+         * @param timestamp $time The last modification time of the cached template
+         */
+        function isFresh($name, $time);
+    }
+
+As an example, here is how the built-in ``Twig_Loader_String`` reads::
+
+    class Twig_Loader_String implements Twig_LoaderInterface
+    {
+        public function getSource($name)
+        {
+          return $name;
+        }
+
+        public function getCacheKey($name)
+        {
+          return $name;
+        }
+
+        public function isFresh($name, $time)
+        {
+          return false;
+        }
+    }
+
+The ``isFresh()`` method must return ``true`` if the current cached template
+is still fresh, given the last modification time, or ``false`` otherwise.
+
+Using Extensions
+----------------
+
+Twig extensions are packages that add new features to Twig. Using an
+extension is as simple as using the ``addExtension()`` method::
+
+    $twig->addExtension(new Twig_Extension_Sandbox());
+
+Twig comes bundled with the following extensions:
+
+* *Twig_Extension_Core*: Defines all the core features of Twig.
+
+* *Twig_Extension_Escaper*: Adds automatic output-escaping and the possibility
+  to escape/unescape blocks of code.
+
+* *Twig_Extension_Sandbox*: Adds a sandbox mode to the default Twig
+  environment, making it safe to evaluated untrusted code.
+
+* *Twig_Extension_Optimizer*: Optimizers the node tree before compilation.
+
+The core, escaper, and optimizer extensions do not need to be added to the
+Twig environment, as they are registered by default. You can disable an
+already registered extension::
+
+    $twig->removeExtension('escaper');
+
+Built-in Extensions
+-------------------
+
+This section describes the features added by the built-in extensions.
+
+.. tip::
+
+    Read the chapter about extending Twig to learn how to create your own
+    extensions.
+
+Core Extension
+~~~~~~~~~~~~~~
+
+The ``core`` extension defines all the core features of Twig:
+
+* Tags:
+
+  * ``for``
+  * ``if``
+  * ``extends``
+  * ``include``
+  * ``block``
+  * ``filter``
+  * ``macro``
+  * ``import``
+  * ``from``
+  * ``set``
+  * ``spaceless``
+
+* Filters:
+
+  * ``date``
+  * ``format``
+  * ``replace``
+  * ``url_encode``
+  * ``json_encode``
+  * ``title``
+  * ``capitalize``
+  * ``upper``
+  * ``lower``
+  * ``striptags``
+  * ``join``
+  * ``reverse``
+  * ``length``
+  * ``sort``
+  * ``merge``
+  * ``default``
+  * ``keys``
+  * ``escape``
+  * ``e``
+
+* Functions:
+
+  * ``range``
+  * ``constant``
+  * ``cycle``
+  * ``parent``
+  * ``block``
+
+* Tests:
+
+  * ``even``
+  * ``odd``
+  * ``defined``
+  * ``sameas``
+  * ``null``
+  * ``divisibleby``
+  * ``constant``
+  * ``empty``
+
+Escaper Extension
+~~~~~~~~~~~~~~~~~
+
+The ``escaper`` extension adds automatic output escaping to Twig. It defines a
+new tag, ``autoescape``, and a new filter, ``raw``.
+
+When creating the escaper extension, you can switch on or off the global
+output escaping strategy::
+
+    $escaper = new Twig_Extension_Escaper(true);
+    $twig->addExtension($escaper);
+
+If set to ``true``, all variables in templates are escaped, except those using
+the ``raw`` filter:
+
+.. code-block:: jinja
+
+    {{ article.to_html|raw }}
+
+You can also change the escaping mode locally by using the ``autoescape`` tag:
+
+.. code-block:: jinja
+
+    {% autoescape true %}
+      {{ var }}
+      {{ var|raw }}      {# var won't be escaped #}
+      {{ var|escape }}   {# var won't be double-escaped #}
+    {% endautoescape %}
+
+.. warning::
+
+    The ``autoescape`` tag has no effect on included files.
+
+The escaping rules are implemented as follows:
+
+* Literals (integers, booleans, arrays, ...) used in the template directly as
+  variables or filter arguments are never automatically escaped:
+
+  .. code-block:: jinja
+
+        {{ "Twig<br />" }} {# won't be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ text }} {# will be escaped #}
+
+* Expressions which the result is always a literal or a variable marked safe
+  are never automatically escaped:
+
+  .. code-block:: jinja
+
+        {{ foo ? "Twig<br />" : "<br />Twig" }} {# won't be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ foo ? text : "<br />Twig" }} {# will be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ foo ? text|raw : "<br />Twig" }} {# won't be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ foo ? text|escape : "<br />Twig" }} {# the result of the expression won't be escaped #}
+
+* Escaping is applied before printing, after any other filter is applied:
+
+  .. code-block:: jinja
+
+        {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #}
+
+* The `raw` filter should only be used at the end of the filter chain:
+
+  .. code-block:: jinja
+
+        {{ var|raw|upper }} {# will be escaped #}
+
+        {{ var|upper|raw }} {# won't be escaped #}
+
+* Automatic escaping is not applied if the last filter in the chain is marked
+  safe for the current context (e.g. ``html`` or ``js``). ``escaper`` and
+  ``escaper('html')`` are marked safe for html, ``escaper('js')`` is marked
+  safe for javascript, ``raw`` is marked safe for everything.
+
+  .. code-block:: jinja
+
+        {% autoescape true js %}
+        {{ var|escape('html') }} {# will be escaped for html and javascript #}
+        {{ var }} {# will be escaped for javascript #}
+        {{ var|escape('js') }} {# won't be double-escaped #}
+        {% endautoescape %}
+
+.. note::
+
+    Note that autoescaping has some limitations as escaping is applied on
+    expressions after evaluation. For instance, when working with
+    concatenation, ``{{ foo|raw ~ bar }}`` won't give the expected result as
+    escaping is applied on the result of the concatenation, not on the
+    individual variables (so, the ``raw`` filter won't have any effect here).
+
+Sandbox Extension
+~~~~~~~~~~~~~~~~~
+
+The ``sandbox`` extension can be used to evaluate untrusted code. Access to
+unsafe attributes and methods is prohibited. The sandbox security is managed
+by a policy instance. By default, Twig comes with one policy class:
+``Twig_Sandbox_SecurityPolicy``. This class allows you to white-list some
+tags, filters, properties, and methods::
+
+    $tags = array('if');
+    $filters = array('upper');
+    $methods = array(
+        'Article' => array('getTitle', 'getBody'),
+    );
+    $properties = array(
+        'Article' => array('title', 'body'),
+    );
+    $functions = array('range');
+    $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
+
+With the previous configuration, the security policy will only allow usage of
+the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be
+able to call the ``getTitle()`` and ``getBody()`` methods on ``Article``
+objects, and the ``title`` and ``body`` public properties. Everything else
+won't be allowed and will generate a ``Twig_Sandbox_SecurityError`` exception.
+
+The policy object is the first argument of the sandbox constructor::
+
+    $sandbox = new Twig_Extension_Sandbox($policy);
+    $twig->addExtension($sandbox);
+
+By default, the sandbox mode is disabled and should be enabled when including
+untrusted template code by using the ``sandbox`` tag:
+
+.. code-block:: jinja
+
+    {% sandbox %}
+        {% include 'user.html' %}
+    {% endsandbox %}
+
+You can sandbox all templates by passing ``true`` as the second argument of
+the extension constructor::
+
+    $sandbox = new Twig_Extension_Sandbox($policy, true);
+
+Optimizer Extension
+~~~~~~~~~~~~~~~~~~~
+
+The ``optimizer`` extension optimizes the node tree before compilation::
+
+    $twig->addExtension(new Twig_Extension_Optimizer());
+
+By default, all optimizations are turned on. You can select the ones you want
+to enable by passing them to the constructor::
+
+    $optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR);
+
+    $twig->addExtension($optimizer);
+
+Exceptions
+----------
+
+Twig can throw exceptions:
+
+* ``Twig_Error``: The base exception for all errors.
+
+* ``Twig_Error_Syntax``: Thrown to tell the user that there is a problem with
+  the template syntax.
+
+* ``Twig_Error_Runtime``: Thrown when an error occurs at runtime (when a filter
+  does not exist for instance).
+
+* ``Twig_Error_Loader``: Thrown when an error occurs during template loading.
+
+* ``Twig_Sandbox_SecurityError``: Thrown when an unallowed tag, filter, or
+  method is called in a sandboxed template.

+ 101 - 0
lib/twig/doc/coding_standards.rst

@@ -0,0 +1,101 @@
+Coding Standards
+================
+
+When writing Twig templates, we recommend you to follow these official coding
+standards:
+
+* Put one (and only one) space after the start of a delimiter (``{{``, ``{%``,
+  and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``):
+
+  .. code-block:: jinja
+
+    {{ foo }}
+    {# comment #}
+    {% if foo %}{% endif %}
+
+  When using the whitespace control character, do not put any spaces between
+  it and the delimiter:
+
+  .. code-block:: jinja
+
+    {{- foo -}}
+    {#- comment -#}
+    {%- if foo -%}{%- endif -%}
+
+* Put one (and only one) space before and after the following operators:
+  comparison operators (``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``), math
+  operators (``+``, ``-``, ``/``, ``*``, ``%``, ``//``, ``**``), logic
+  operators (``not``, ``and``, ``or``), ``~``, ``is``, ``in``, and the ternary
+  operator (``?:``):
+
+  .. code-block:: jinja
+
+     {{ 1 + 2 }}
+     {{ foo ~ bar }}
+     {{ true ? true : false }}
+
+* Put one (and only one) space after the ``:`` sign in hashes and ``,`` in
+  arrays and hashes:
+
+  .. code-block:: jinja
+
+     {{ [1, 2, 3] }}
+     {{ {'foo': 'bar'} }}
+
+* Do not put any spaces after an opening parenthesis and before a closing
+  parenthesis in expressions:
+
+  .. code-block:: jinja
+
+    {{ 1 + (2 * 3) }}
+
+* Do not put any spaces before and after string delimiters:
+
+  .. code-block:: jinja
+
+    {{ 'foo' }}
+    {{ "foo" }}
+
+* Do not put any spaces before and after the following operators: ``|``,
+  ``.``, ``..``, ``[]``:
+
+  .. code-block:: jinja
+
+    {{ foo|upper|lower }}
+    {{ user.name }}
+    {{ user[name] }}
+    {% for i in 1..12 %}{% endfor %}
+
+* Do not put any spaces before and after the parenthesis used for filter and
+  function calls:
+
+  .. code-block:: jinja
+
+     {{ foo|default('foo') }}
+     {{ range(1..10) }}
+
+* Do not put any spaces before and after the opening and the closing of arrays
+  and hashes:
+
+  .. code-block:: jinja
+
+     {{ [1, 2, 3] }}
+     {{ {'foo': 'bar'} }}
+
+* Use lower cased and underscored variable names:
+
+  .. code-block:: jinja
+
+     {% set foo = 'foo' %}
+     {% set foo_bar = 'foo' %}
+
+* Indent your code inside tags (use the same indentation as the one used for
+  the main language of the file):
+
+  .. code-block:: jinja
+
+     {% block foo %}
+        {% if true %}
+            true
+        {% endif %}
+     {% endblock %}

+ 328 - 0
lib/twig/doc/extensions.rst

@@ -0,0 +1,328 @@
+Creating a Twig Extension
+=========================
+
+The main motivation for writing an extension is to move often used code into a
+reusable class like adding support for internationalization. An extension can
+define tags, filters, tests, operators, global variables, functions, and node
+visitors.
+
+Creating an extension also makes for a better separation of code that is
+executed at compilation time and code needed at runtime. As such, it makes
+your code faster.
+
+Most of the time, it is useful to create a single extension for your project,
+to host all the specific tags and filters you want to add to Twig.
+
+.. note::
+
+    Before writing your own extensions, have a look at the Twig official
+    extension repository: http://github.com/fabpot/Twig-extensions.
+
+An extension is a class that implements the following interface::
+
+    interface Twig_ExtensionInterface
+    {
+        /**
+         * Initializes the runtime environment.
+         *
+         * This is where you can load some file that contains filter functions for instance.
+         *
+         * @param Twig_Environment $environment The current Twig_Environment instance
+         */
+        function initRuntime(Twig_Environment $environment);
+
+        /**
+         * Returns the token parser instances to add to the existing list.
+         *
+         * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
+         */
+        function getTokenParsers();
+
+        /**
+         * Returns the node visitor instances to add to the existing list.
+         *
+         * @return array An array of Twig_NodeVisitorInterface instances
+         */
+        function getNodeVisitors();
+
+        /**
+         * Returns a list of filters to add to the existing list.
+         *
+         * @return array An array of filters
+         */
+        function getFilters();
+
+        /**
+         * Returns a list of tests to add to the existing list.
+         *
+         * @return array An array of tests
+         */
+        function getTests();
+
+        /**
+         * Returns a list of functions to add to the existing list.
+         *
+         * @return array An array of functions
+         */
+        function getFunctions();
+
+        /**
+         * Returns a list of operators to add to the existing list.
+         *
+         * @return array An array of operators
+         */
+        function getOperators();
+
+        /**
+         * Returns a list of global variables to add to the existing list.
+         *
+         * @return array An array of global variables
+         */
+        function getGlobals();
+
+        /**
+         * Returns the name of the extension.
+         *
+         * @return string The extension name
+         */
+        function getName();
+    }
+
+To keep your extension class clean and lean, it can inherit from the built-in
+``Twig_Extension`` class instead of implementing the whole interface. That
+way, you just need to implement the ``getName()`` method as the
+``Twig_Extension`` provides empty implementations for all other methods.
+
+The ``getName()`` method must return a unique identifier for your extension.
+
+Now, with this information in mind, let's create the most basic extension
+possible::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getName()
+        {
+            return 'project';
+        }
+    }
+
+.. note::
+
+    Of course, this extension does nothing for now. We will customize it in
+    the next sections.
+
+Twig does not care where you save your extension on the filesystem, as all
+extensions must be registered explicitly to be available in your templates.
+
+You can register an extension by using the ``addExtension()`` method on your
+main ``Environment`` object::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addExtension(new Project_Twig_Extension());
+
+Of course, you need to first load the extension file by either using
+``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
+
+.. tip::
+
+    The bundled extensions are great examples of how extensions work.
+
+Globals
+-------
+
+Global variables can be registered in an extension via the ``getGlobals()``
+method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getGlobals()
+        {
+            return array(
+                'text' => new Text(),
+            );
+        }
+
+        // ...
+    }
+
+Functions
+---------
+
+Functions can be registered in an extension via the ``getFunctions()``
+method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFunctions()
+        {
+            return array(
+                'lipsum' => new Twig_Function_Function('generate_lipsum'),
+            );
+        }
+
+        // ...
+    }
+
+Filters
+-------
+
+To add a filter to an extension, you need to override the ``getFilters()``
+method. This method must return an array of filters to add to the Twig
+environment::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFilters()
+        {
+            return array(
+                'rot13' => new Twig_Filter_Function('str_rot13'),
+            );
+        }
+
+        // ...
+    }
+
+As you can see in the above code, the ``getFilters()`` method returns an array
+where keys are the name of the filters (``rot13``) and the values the
+definition of the filter (``new Twig_Filter_Function('str_rot13')``).
+
+As seen in the previous chapter, you can also define filters as static methods
+on the extension class::
+
+$twig->addFilter('rot13', new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'));
+
+You can also use ``Twig_Filter_Method`` instead of ``Twig_Filter_Function``
+when defining a filter to use a method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFilters()
+        {
+            return array(
+                'rot13' => new Twig_Filter_Method($this, 'rot13Filter'),
+            );
+        }
+
+        public function rot13Filter($string)
+        {
+            return str_rot13($string);
+        }
+
+        // ...
+    }
+
+The first argument of the ``Twig_Filter_Method`` constructor is always
+``$this``, the current extension object. The second one is the name of the
+method to call.
+
+Using methods for filters is a great way to package your filter without
+polluting the global namespace. This also gives the developer more flexibility
+at the cost of a small overhead.
+
+Overriding default Filters
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If some default core filters do not suit your needs, you can easily override
+them by creating your own core extension. Of course, you don't need to copy
+and paste the whole core extension code of Twig. Instead, you can just extends
+it and override the filter(s) you want by overriding the ``getFilters()``
+method::
+
+    class MyCoreExtension extends Twig_Extension_Core
+    {
+        public function getFilters()
+        {
+            return array_merge(parent::getFilters(), array(
+                'date' => new Twig_Filter_Method($this, 'dateFilter'),
+                // ...
+            ));
+        }
+
+        public function dateFilter($timestamp, $format = 'F j, Y H:i')
+        {
+            return '...'.twig_date_format_filter($timestamp, $format);
+        }
+
+        // ...
+    }
+
+Here, we override the ``date`` filter with a custom one. Using this new core
+extension is as simple as registering the ``MyCoreExtension`` extension by
+calling the ``addExtension()`` method on the environment instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addExtension(new MyCoreExtension());
+
+But I can already hear some people wondering how it can work as the Core
+extension is loaded by default. That's true, but the trick is that both
+extensions share the same unique identifier (``core`` - defined in the
+``getName()`` method). By registering an extension with the same name as an
+existing one, you have actually overridden the default one, even if it is
+already registered::
+
+    $twig->addExtension(new Twig_Extension_Core());
+    $twig->addExtension(new MyCoreExtension());
+
+Tags
+----
+
+Adding a tag in an extension can be done by overriding the
+``getTokenParsers()`` method. This method must return an array of tags to add
+to the Twig environment::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getTokenParsers()
+        {
+            return array(new Project_Set_TokenParser());
+        }
+
+        // ...
+    }
+
+In the above code, we have added a single new tag, defined by the
+``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
+responsible for parsing the tag and compiling it to PHP.
+
+Operators
+---------
+
+The ``getOperators()`` methods allows to add new operators. Here is how to add
+``!``, ``||``, and ``&&`` operators::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getOperators()
+        {
+            return array(
+                array(
+                    '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
+                ),
+                array(
+                    '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+                    '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+                ),
+            );
+        }
+
+        // ...
+    }
+
+Tests
+-----
+
+The ``getTests()`` methods allows to add new test functions::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getTests()
+        {
+            return array(
+                'even' => new Twig_Test_Function('twig_test_even'),
+            );
+        }
+
+        // ...
+    }
+
+.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register

+ 11 - 0
lib/twig/doc/filters/capitalize.rst

@@ -0,0 +1,11 @@
+``capitalize``
+==============
+
+The ``capitalize`` filter capitalizes a value. The first character will be
+uppercase, all others lowercase:
+
+.. code-block:: jinja
+
+    {{ 'my first car'|capitalize }}
+
+    {# outputs 'My first car' #}

+ 22 - 0
lib/twig/doc/filters/convert_encoding.rst

@@ -0,0 +1,22 @@
+``convert_encoding``
+====================
+
+.. versionadded:: 1.4
+    The ``convert_encoding`` filter was added in Twig 1.4.
+
+The ``convert_encoding`` filter converts a string from one encoding to
+another. The first argument is the expected output charset and the second one
+is the input charset:
+
+.. code-block:: jinja
+
+    {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
+
+.. note::
+
+    This filter relies on the `iconv`_ or `mbstring`_ extension, so one of
+    them must be installed. In case both are installed, `iconv`_ is used
+    by default.
+
+.. _`iconv`:    http://php.net/iconv
+.. _`mbstring`: http://php.net/mbstring

+ 65 - 0
lib/twig/doc/filters/date.rst

@@ -0,0 +1,65 @@
+``date``
+========
+
+.. versionadded:: 1.1
+    The timezone support has been added in Twig 1.1.
+
+.. versionadded:: 1.5
+    The default date format support has been added in Twig 1.5.
+
+.. versionadded:: 1.6.1
+    The default timezone support has been added in Twig 1.6.1.
+
+The ``date`` filter formats a date to a given format:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date("m/d/Y") }}
+
+The ``date`` filter accepts strings (it must be in a format supported by the
+`date`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For
+instance, to display the current date, filter the word "now":
+
+.. code-block:: jinja
+
+    {{ "now"|date("m/d/Y") }}
+
+To escape words and characters in the date format use ``\\`` in front of each character:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date("F jS \\a\\t g:ia") }}
+
+You can also specify a timezone:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date("m/d/Y", "Europe/Paris") }}
+
+If no format is provided, Twig will use the default one: ``F j, Y H:i``. This
+default can be easily changed by calling the ``setDateFormat()`` method on the
+``core`` extension instance. The first argument is the default format for
+dates and the second one is the default format for date intervals:
+
+.. code-block:: php
+
+    $twig = new Twig_Environment($loader);
+    $twig->getExtension('core')->setDateFormat('d/m/Y', '%d days');
+
+The default timezone can also be set globally by calling ``setTimezone()``:
+
+.. code-block:: php
+
+    $twig = new Twig_Environment($loader);
+    $twig->getExtension('core')->setTimezone('Europe/Paris');
+
+.. _`date`:         http://www.php.net/date
+.. _`DateTime`:     http://www.php.net/DateTime
+.. _`DateInterval`: http://www.php.net/DateInterval
+
+If the value passed to the ``date`` filter is null, it will return the current date by default.
+If an empty string is desired instead of the current date, use a ternary operator:
+
+.. code-block:: jinja
+
+    {{ post.published_at is empty ? "" : post.published_at|date("m/d/Y") }}

+ 28 - 0
lib/twig/doc/filters/default.rst

@@ -0,0 +1,28 @@
+``default``
+===========
+
+The ``default`` filter returns the passed default value if the value is
+undefined or empty, otherwise the value of the variable:
+
+.. code-block:: jinja
+
+    {{ var|default('var is not defined') }}
+
+    {{ var.foo|default('foo item on var is not defined') }}
+
+    {{ var['foo']|default('foo item on var is not defined') }}
+
+    {{ ''|default('passed var is empty')  }}
+
+When using the ``default`` filter on an expression that uses variables in some
+method calls, be sure to use the ``default`` filter whenever a variable can be
+undefined:
+
+.. code-block:: jinja
+
+    {{ var.method(foo|default('foo'))|default('foo') }}
+
+.. note::
+
+    Read the documentation for the :doc:`defined<../tests/defined>` and
+    :doc:`empty<../tests/empty>` tests to learn more about their semantics.

+ 30 - 0
lib/twig/doc/filters/escape.rst

@@ -0,0 +1,30 @@
+``escape``
+==========
+
+The ``escape`` filter converts the characters ``&``, ``<``, ``>``, ``'``, and
+``"`` in strings to HTML-safe sequences. Use this if you need to display text
+that might contain such characters in HTML:
+
+.. code-block:: jinja
+
+    {{ user.username|escape }}
+
+For convenience, the ``e`` filter is defined as an alias:
+
+.. code-block:: jinja
+
+    {{ user.username|e }}
+
+The ``escape`` filter can also be used in another context than HTML; for
+instance, to escape variables included in a JavaScript:
+
+.. code-block:: jinja
+
+    {{ user.username|escape('js') }}
+    {{ user.username|e('js') }}
+
+.. note::
+
+    Internally, ``escape`` uses the PHP native `htmlspecialchars`_ function.
+
+.. _`htmlspecialchars`: http://php.net/htmlspecialchars

+ 16 - 0
lib/twig/doc/filters/format.rst

@@ -0,0 +1,16 @@
+``format``
+==========
+
+The ``format`` filter formats a given string by replacing the placeholders
+(placeholders follows the `printf`_ notation):
+
+.. code-block:: jinja
+
+    {{ "I like %s and %s."|format(foo, "bar") }}
+
+    {# returns I like foo and bar
+       if the foo parameter equals to the foo string. #}
+
+.. _`printf`: http://www.php.net/printf
+
+.. seealso:: :doc:`replace<replace>`

+ 29 - 0
lib/twig/doc/filters/index.rst

@@ -0,0 +1,29 @@
+Filters
+=======
+
+.. toctree::
+    :maxdepth: 1
+
+    date
+    format
+    replace
+    number_format
+    url_encode
+    json_encode
+    convert_encoding
+    title
+    capitalize
+    nl2br
+    upper
+    lower
+    striptags
+    join
+    reverse
+    length
+    sort
+    default
+    keys
+    escape
+    raw
+    merge
+    slice

+ 18 - 0
lib/twig/doc/filters/join.rst

@@ -0,0 +1,18 @@
+``join``
+========
+
+The ``join`` filter returns a string which is the concatenation of the items
+of a sequence:
+
+.. code-block:: jinja
+
+    {{ [1, 2, 3]|join }}
+    {# returns 123 #}
+
+The separator between elements is an empty string per default, but you can
+define it with the optional first parameter:
+
+.. code-block:: jinja
+
+    {{ [1, 2, 3]|join('|') }}
+    {# returns 1|2|3 #}

+ 14 - 0
lib/twig/doc/filters/json_encode.rst

@@ -0,0 +1,14 @@
+``json_encode``
+===============
+
+The ``json_encode`` filter returns the JSON representation of a string:
+
+.. code-block:: jinja
+
+    {{ data|json_encode() }}
+
+.. note::
+
+    Internally, Twig uses the PHP `json_encode`_ function.
+
+.. _`json_encode`: http://php.net/json_encode

+ 11 - 0
lib/twig/doc/filters/keys.rst

@@ -0,0 +1,11 @@
+``keys``
+========
+
+The ``keys`` filter returns the keys of an array. It is useful when you want to
+iterate over the keys of an array:
+
+.. code-block:: jinja
+
+    {% for key in array|keys %}
+        ...
+    {% endfor %}

+ 12 - 0
lib/twig/doc/filters/length.rst

@@ -0,0 +1,12 @@
+``length``
+==========
+
+The ``length`` filters returns the number of items of a sequence or mapping, or
+the length of a string:
+
+.. code-block:: jinja
+
+    {% if users|length > 10 %}
+        ...
+    {% endif %}
+

+ 10 - 0
lib/twig/doc/filters/lower.rst

@@ -0,0 +1,10 @@
+``lower``
+=========
+
+The ``lower`` filter converts a value to lowercase:
+
+.. code-block:: jinja
+
+    {{ 'WELCOME'|lower }}
+
+    {# outputs 'welcome' #}

+ 41 - 0
lib/twig/doc/filters/merge.rst

@@ -0,0 +1,41 @@
+``merge``
+=========
+
+The ``merge`` filter merges an array with the another array:
+
+.. code-block:: jinja
+
+    {% set values = [1, 2] %}
+
+    {% set values = values|merge(['apple', 'orange']) %}
+
+    {# values now contains [1, 2, 'apple', 'orange'] #}
+
+New values are added at the end of the existing ones.
+
+The ``merge`` filter also works on hashes:
+
+.. code-block:: jinja
+
+    {% set items = { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'unknown' } %}
+
+    {% set items = items|merge({ 'peugeot': 'car', 'renault': 'car' }) %}
+
+    {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'renault': 'car' } #}
+
+For hashes, the merging process occurs on the keys: if the key does not
+already exist, it is added but if the key already exists, its value is
+overridden.
+
+.. tip::
+
+    If you want to ensure that some values are defined in an array (by given
+    default values), reverse the two elements in the call:
+
+    .. code-block:: jinja
+
+        {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
+
+        {% set items = { 'apple': 'unknown' }|merge(items) %}
+
+        {# items now contains { 'apple': 'fruit', 'orange': 'fruit' } #}

+ 22 - 0
lib/twig/doc/filters/nl2br.rst

@@ -0,0 +1,22 @@
+``nl2br``
+=========
+
+.. versionadded:: 1.5
+    The nl2br filter was added in Twig 1.5.
+
+The ``nl2br`` filter inserts HTML line breaks before all newlines in a string:
+
+.. code-block:: jinja
+
+    {{ "I like Twig.\nYou will like it too."|nl2br }}
+    {# outputs
+
+        I like Twig.<br />
+        You will like it too.
+
+    #}
+
+.. note::
+
+    The ``nl2br`` filter pre-escapes the input before applying the
+    transformation.

+ 38 - 0
lib/twig/doc/filters/number_format.rst

@@ -0,0 +1,38 @@
+``number_format``
+=================
+
+.. versionadded:: 1.5
+    The number_format filter was added in Twig 1.5
+
+The ``number_format`` filter formats numbers.  It is a wrapper around PHP's
+`number_format`_ function:
+
+.. code-block:: jinja
+
+    {{ 200.35|number_format }}
+
+You can control the number of decimal places, decimal point, and thousands
+separator using the additional arguments:
+
+.. code-block:: jinja
+
+    {{ 9800.333|number_format(2, ',', '.') }}
+
+If no formatting options are provided then Twig will use the default formatting
+options of:
+
+- 0 decimal places.
+- ``.`` as the decimal point.
+- ``,`` as the thousands separator.
+
+These defaults can be easily changed through the core extension:
+
+.. code-block:: php
+
+    $twig = new Twig_Environment($loader);
+    $twig->getExtension('core')->setNumberFormat(3, ',', '.');
+
+The defaults set for ``number_format`` can be over-ridden upon each call using the
+additional parameters.
+
+.. _`number_format`: http://php.net/number_format

+ 12 - 0
lib/twig/doc/filters/raw.rst

@@ -0,0 +1,12 @@
+``raw``
+=======
+
+The ``raw`` filter marks the value as being "safe", which means that in an
+environment with automatic escaping enabled this variable will not be escaped
+if ``raw`` is the last filter applied to it:
+
+.. code-block:: jinja
+
+    {% autoescape true %}
+        {{ var|raw }} {# var won't be escaped #}
+    {% endautoescape %}

+ 14 - 0
lib/twig/doc/filters/replace.rst

@@ -0,0 +1,14 @@
+``replace``
+===========
+
+The ``replace`` filter formats a given string by replacing the placeholders
+(placeholders are free-form):
+
+.. code-block:: jinja
+
+    {{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }}
+
+    {# returns I like foo and bar
+       if the foo parameter equals to the foo string. #}
+
+.. seealso:: :doc:`format<format>`

+ 23 - 0
lib/twig/doc/filters/reverse.rst

@@ -0,0 +1,23 @@
+``reverse``
+===========
+
+.. versionadded:: 1.6
+    Support for strings has been added in Twig 1.6.
+
+The ``reverse`` filter reverses a sequence, a mapping, or a string:
+
+.. code-block:: jinja
+
+    {% for use in users|reverse %}
+        ...
+    {% endfor %}
+
+    {{ '1234'|reverse }}
+
+    {# outputs 4321 #}
+
+.. note::
+
+    It also works with objects implementing the `Traversable`_ interface.
+
+.. _`Traversable`: http://php.net/Traversable

+ 57 - 0
lib/twig/doc/filters/slice.rst

@@ -0,0 +1,57 @@
+``slice``
+===========
+
+.. versionadded:: 1.6
+    The slice filter was added in Twig 1.6.
+
+The ``slice`` filter extracts a slice of a sequence, a mapping, or a string:
+
+.. code-block:: jinja
+
+    {% for i in [1, 2, 3, 4]|slice(1, 2) %}
+        {# will iterate over 2 and 3 #}
+    {% endfor %}
+
+    {{ '1234'|slice(1, 2) }}
+
+    {# outputs 23 #}
+
+You can use any valid expression for both the start and the length:
+
+.. code-block:: jinja
+
+    {% for i in [1, 2, 3, 4]|slice(start, length) %}
+        {# ... #}
+    {% endfor %}
+
+As syntactic sugar, you can also use the ``[]`` notation:
+
+.. code-block:: jinja
+
+    {% for i in [1, 2, 3, 4][start:length] %}
+        {# ... #}
+    {% endfor %}
+
+    {{ '1234'[1:2] }}
+
+The ``slice`` filter works as the `array_slice`_ PHP function for arrays and
+`substr`_ for strings.
+
+If the start is non-negative, the sequence will start at that start in the
+variable. If start is negative, the sequence will start that far from the end
+of the variable.
+
+If length is given and is positive, then the sequence will have up to that
+many elements in it. If the variable is shorter than the length, then only the
+available variable elements will be present. If length is given and is
+negative then the sequence will stop that many elements from the end of the
+variable. If it is omitted, then the sequence will have everything from offset
+up until the end of the variable.
+
+.. note::
+
+    It also works with objects implementing the `Traversable`_ interface.
+
+.. _`Traversable`: http://php.net/manual/en/class.traversable.php
+.. _`array_slice`: http://php.net/array_slice
+.. _`substr`:      http://php.net/substr

+ 17 - 0
lib/twig/doc/filters/sort.rst

@@ -0,0 +1,17 @@
+``sort``
+========
+
+The ``sort`` filter sorts an array:
+
+.. code-block:: jinja
+
+    {% for use in users|sort %}
+        ...
+    {% endfor %}
+
+.. note::
+
+    Internally, Twig uses the PHP `asort`_ function to maintain index
+    association.
+
+.. _`asort`: http://php.net/asort

+ 15 - 0
lib/twig/doc/filters/striptags.rst

@@ -0,0 +1,15 @@
+``striptags``
+=============
+
+The ``striptags`` filter strips SGML/XML tags and replace adjacent whitespace
+by one space:
+
+.. code-block:: jinja
+
+    {% some_html|striptags %}
+
+.. note::
+
+    Internally, Twig uses the PHP `strip_tags`_ function.
+
+.. _`strip_tags`: http://php.net/strip_tags

+ 11 - 0
lib/twig/doc/filters/title.rst

@@ -0,0 +1,11 @@
+``title``
+=========
+
+The ``title`` filter returns a titlecased version of the value. Words will
+start with uppercase letters, all remaining characters are lowercase:
+
+.. code-block:: jinja
+
+    {{ 'my first car'|title }}
+
+    {# outputs 'My First Car' #}

+ 24 - 0
lib/twig/doc/filters/trim.rst

@@ -0,0 +1,24 @@
+``trim``
+========
+
+.. versionadded:: 1.6.2
+    The trim filter was added in Twig 1.6.2.
+
+The ``trim`` filter strips whitespace (or other characters) from the beginning
+and end of a string:
+
+.. code-block:: jinja
+
+    {{ '  I like Twig.  '|trim }}
+
+    {# outputs 'I like Twig.' #}
+
+    {{ '  I like Twig.'|trim('.') }}
+
+    {# outputs '  I like Twig' #}
+
+.. note::
+
+    Internally, Twig uses the PHP `trim`_ function.
+
+.. _`trim`: http://php.net/trim

+ 10 - 0
lib/twig/doc/filters/upper.rst

@@ -0,0 +1,10 @@
+``upper``
+=========
+
+The ``upper`` filter converts a value to uppercase:
+
+.. code-block:: jinja
+
+    {{ 'welcome'|upper }}
+
+    {# outputs 'WELCOME' #}

+ 14 - 0
lib/twig/doc/filters/url_encode.rst

@@ -0,0 +1,14 @@
+``url_encode``
+==============
+
+The ``url_encode`` filter URL encodes a given string:
+
+.. code-block:: jinja
+
+    {{ data|url_encode() }}
+
+.. note::
+
+    Internally, Twig uses the PHP `urlencode`_ function.
+
+.. _`urlencode`: http://php.net/urlencode

+ 18 - 0
lib/twig/doc/functions/attribute.rst

@@ -0,0 +1,18 @@
+``attribute``
+=============
+
+.. versionadded:: 1.2
+    The ``attribute`` function was added in Twig 1.2.
+
+``attribute`` can be used to access a "dynamic" attribute of a variable:
+
+.. code-block:: jinja
+
+    {{ attribute(object, method) }}
+    {{ attribute(object, method, arguments) }}
+    {{ attribute(array, item) }}
+
+.. note::
+
+    The resolution algorithm is the same as the one used for the ``.``
+    notation, except that the item can be any valid expression.

+ 15 - 0
lib/twig/doc/functions/block.rst

@@ -0,0 +1,15 @@
+``block``
+=========
+
+When a template uses inheritance and if you want to print a block multiple
+times, use the ``block`` function:
+
+.. code-block:: jinja
+
+    <title>{% block title %}{% endblock %}</title>
+
+    <h1>{{ block('title') }}</h1>
+
+    {% block body %}{% endblock %}
+
+.. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`

+ 9 - 0
lib/twig/doc/functions/constant.rst

@@ -0,0 +1,9 @@
+``constant``
+============
+
+``constant`` returns the constant value for a given string:
+
+.. code-block:: jinja
+
+    {{ some_date|date(constant('DATE_W3C')) }}
+    {{ constant('Namespace\\Classname::CONSTANT_NAME') }}

+ 20 - 0
lib/twig/doc/functions/cycle.rst

@@ -0,0 +1,20 @@
+``cycle``
+=========
+
+The ``cycle`` function cycles on an array of values:
+
+.. code-block:: jinja
+
+    {% for i in 0..10 %}
+        {{ cycle(['odd', 'even'], i) }}
+    {% endfor %}
+
+The array can contain any number of values:
+
+.. code-block:: jinja
+
+    {% set fruits = ['apple', 'orange', 'citrus'] %}
+
+    {% for i in 0..10 %}
+        {{ cycle(fruits, i) }}
+    {% endfor %}

+ 46 - 0
lib/twig/doc/functions/date.rst

@@ -0,0 +1,46 @@
+``date``
+========
+
+.. versionadded:: 1.6
+    The date function has been added in Twig 1.6.
+
+.. versionadded:: 1.6.1
+    The default timezone support has been added in Twig 1.6.1.
+
+Converts an argument to a date to allow date comparison:
+
+.. code-block:: jinja
+
+    {% if date(user.created_at) < date('+2days') %}
+        {# do something #}
+    {% endif %}
+
+The argument must be in a format supported by the `date`_ function.
+
+You can pass a timezone as the second argument:
+
+.. code-block:: jinja
+
+    {% if date(user.created_at) < date('+2days', 'Europe/Paris') %}
+        {# do something #}
+    {% endif %}
+
+If no argument is passed, the function returns the current date:
+
+.. code-block:: jinja
+
+    {% if date(user.created_at) < date() %}
+        {# always! #}
+    {% endif %}
+
+.. note::
+
+    You can set the default timezone globally by calling ``setTimezone()`` on
+    the ``core`` extension instance:
+
+    .. code-block:: php
+
+        $twig = new Twig_Environment($loader);
+        $twig->getExtension('core')->setTimezone('Europe/Paris');
+
+.. _`date`: http://www.php.net/date

+ 58 - 0
lib/twig/doc/functions/dump.rst

@@ -0,0 +1,58 @@
+``dump``
+========
+
+.. versionadded:: 1.5
+    The dump function was added in Twig 1.5.
+
+The ``dump`` function dumps information about a template variable. This is
+mostly useful to debug a template that does not behave as expected by
+introspecting its variables:
+
+.. code-block:: jinja
+
+    {{ dump(user) }}
+
+.. note::
+
+    The ``debug`` function is not available by default. You must load it explicitly::
+
+        $twig = new Twig_Environment($loader, $config);
+        $twig->addExtension(new Twig_Extension_Debug());
+
+    Even when loaded explicitly, it won't do anything if the ``debug`` option
+    is not enabled.
+
+In an HTML context, wrap the output with a ``pre`` tag to make it easier to
+read:
+
+.. code-block:: jinja
+
+    <pre>
+        {{ dump(user) }}
+    </pre>
+
+.. tip::
+
+    Using a ``pre`` tag is not needed when `XDebug`_ is enabled and
+    ``html_errors`` is ``on``; as a bonus, the output is also nicer with
+    XDebug enabled.
+
+You can debug several variables by passing them as additional arguments:
+
+.. code-block:: jinja
+
+    {{ dump(user, categories) }}
+
+If you don't pass any value, all variables from the current context are
+dumped:
+
+.. code-block:: jinja
+
+    {{ dump() }}
+
+.. note::
+
+    Internally, Twig uses the PHP `var_dump`_ function.
+
+.. _`XDebug`: http://xdebug.org/docs/display
+.. _`var_dump`: http://php.net/var_dump

+ 15 - 0
lib/twig/doc/functions/index.rst

@@ -0,0 +1,15 @@
+Functions
+=========
+
+.. toctree::
+    :maxdepth: 1
+
+    range
+    cycle
+    constant
+    random
+    attribute
+    block
+    parent
+    dump
+    date

+ 20 - 0
lib/twig/doc/functions/parent.rst

@@ -0,0 +1,20 @@
+``parent``
+==========
+
+When a template uses inheritance, it's possible to render the contents of the
+parent block when overriding a block by using the ``parent`` function:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block sidebar %}
+        <h3>Table Of Contents</h3>
+        ...
+        {{ parent() }}
+    {% endblock %}
+
+The ``parent()`` call will return the content of the ``sidebar`` block as
+defined in the ``base.html`` template.
+
+.. seealso:: :doc:`extends<../tags/extends>`, :doc:`block<../functions/block>`, :doc:`block<../tags/block>`

+ 24 - 0
lib/twig/doc/functions/random.rst

@@ -0,0 +1,24 @@
+``random``
+==========
+
+.. versionadded:: 1.5
+    The random function was added in Twig 1.5.
+
+.. versionadded:: 1.6
+    String and integer handling was added in Twig 1.6.
+
+The ``random`` function returns a random value depending on the supplied
+parameter type:
+
+* a random item from a sequence;
+* a random character from a string;
+* a random integer between 0 and the integer parameter (inclusive).
+
+.. code-block:: jinja
+
+    {{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #}
+    {{ random('ABC') }}                         {# example output: C #}
+    {{ random() }}                              {# example output: 15386094 (works as native PHP `mt_rand`_ function) #}
+    {{ random(5) }}                             {# example output: 3 #}
+
+.. _`mt_rand`: http://php.net/mt_rand

+ 38 - 0
lib/twig/doc/functions/range.rst

@@ -0,0 +1,38 @@
+``range``
+=========
+
+Returns a list containing an arithmetic progression of integers:
+
+.. code-block:: jinja
+
+    {% for i in range(0, 3) %}
+        {{ i }},
+    {% endfor %}
+
+    {# returns 0, 1, 2, 3 #}
+
+When step is given (as the third parameter), it specifies the increment (or
+decrement):
+
+.. code-block:: jinja
+
+    {% for i in range(0, 6, 2) %}
+        {{ i }},
+    {% endfor %}
+
+    {# returns 0, 2, 4, 6 #}
+
+The Twig built-in ``..`` operator is just syntactic sugar for the ``range``
+function (with a step of 1):
+
+.. code-block:: jinja
+
+    {% for i in 0..3 %}
+        {{ i }},
+    {% endfor %}
+
+.. tip::
+
+    The ``range`` function works as the native PHP `range`_ function.
+
+.. _`range`: http://php.net/range

+ 184 - 0
lib/twig/doc/hacking.rst

@@ -0,0 +1,184 @@
+Hacking Twig
+============
+
+Twig is very extensible and you can easily hack it. Keep in mind that you
+should probably try to create an extension before hacking the core, as most
+features and enhancements can be done with extensions. This chapter is also
+useful for people who want to understand how Twig works under the hood.
+
+How Twig works?
+---------------
+
+The rendering of a Twig template can be summarized into four key steps:
+
+* **Load** the template: If the template is already compiled, load it and go
+  to the *evaluation* step, otherwise:
+
+  * First, the **lexer** tokenizes the template source code into small pieces
+    for easier processing;
+  * Then, the **parser** converts the token stream into a meaningful tree
+    of nodes (the Abstract Syntax Tree);
+  * Eventually, the *compiler* transforms the AST into PHP code;
+
+* **Evaluate** the template: It basically means calling the ``display()``
+  method of the compiled template and passing it the context.
+
+The Lexer
+---------
+
+The Twig lexer goal is to tokenize a source code into a token stream (each
+token is of class ``Token``, and the stream is an instance of
+``Twig_TokenStream``). The default lexer recognizes nine different token types:
+
+* ``Twig_Token::TEXT_TYPE``
+* ``Twig_Token::BLOCK_START_TYPE``
+* ``Twig_Token::VAR_START_TYPE``
+* ``Twig_Token::BLOCK_END_TYPE``
+* ``Twig_Token::VAR_END_TYPE``
+* ``Twig_Token::NAME_TYPE``
+* ``Twig_Token::NUMBER_TYPE``
+* ``Twig_Token::STRING_TYPE``
+* ``Twig_Token::OPERATOR_TYPE``
+* ``Twig_Token::EOF_TYPE``
+
+You can manually convert a source code into a token stream by calling the
+``tokenize()`` of an environment::
+
+    $stream = $twig->tokenize($source, $identifier);
+
+As the stream has a ``__toString()`` method, you can have a textual
+representation of it by echoing the object::
+
+    echo $stream."\n";
+
+Here is the output for the ``Hello {{ name }}`` template:
+
+.. code-block:: text
+
+    TEXT_TYPE(Hello )
+    VAR_START_TYPE()
+    NAME_TYPE(name)
+    VAR_END_TYPE()
+    EOF_TYPE()
+
+You can change the default lexer use by Twig (``Twig_Lexer``) by calling the
+``setLexer()`` method::
+
+    $twig->setLexer($lexer);
+
+Lexer classes must implement the ``Twig_LexerInterface``::
+
+    interface Twig_LexerInterface
+    {
+        /**
+         * Tokenizes a source code.
+         *
+         * @param  string $code     The source code
+         * @param  string $filename A unique identifier for the source code
+         *
+         * @return Twig_TokenStream A token stream instance
+         */
+        function tokenize($code, $filename = 'n/a');
+    }
+
+The Parser
+----------
+
+The parser converts the token stream into an AST (Abstract Syntax Tree), or a
+node tree (of class ``Twig_Node_Module``). The core extension defines the
+basic nodes like: ``for``, ``if``, ... and the expression nodes.
+
+You can manually convert a token stream into a node tree by calling the
+``parse()`` method of an environment::
+
+    $nodes = $twig->parse($stream);
+
+Echoing the node object gives you a nice representation of the tree::
+
+    echo $nodes."\n";
+
+Here is the output for the ``Hello {{ name }}`` template:
+
+.. code-block:: text
+
+    Twig_Node_Module(
+      Twig_Node_Text(Hello )
+      Twig_Node_Print(
+        Twig_Node_Expression_Name(name)
+      )
+    )
+
+The default parser (``Twig_TokenParser``) can be also changed by calling the
+``setParser()`` method::
+
+    $twig->setParser($parser);
+
+All Twig parsers must implement the ``Twig_ParserInterface``::
+
+    interface Twig_ParserInterface
+    {
+        /**
+         * Converts a token stream to a node tree.
+         *
+         * @param  Twig_TokenStream $stream A token stream instance
+         *
+         * @return Twig_Node_Module A node tree
+         */
+        function parser(Twig_TokenStream $code);
+    }
+
+The Compiler
+------------
+
+The last step is done by the compiler. It takes a node tree as an input and
+generates PHP code usable for runtime execution of the templates. The default
+compiler generates PHP classes to ease the implementation of the template
+inheritance feature.
+
+You can call the compiler by hand with the ``compile()`` method of an
+environment::
+
+    $php = $twig->compile($nodes);
+
+The ``compile()`` method returns the PHP source code representing the node.
+
+The generated template for a ``Hello {{ name }}`` template reads as follows::
+
+    /* Hello {{ name }} */
+    class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template
+    {
+        public function display($context)
+        {
+            $this->env->initRuntime();
+
+            // line 1
+            echo "Hello ";
+            echo (isset($context['name']) ? $context['name'] : null);
+        }
+    }
+
+As for the lexer and the parser, the default compiler (``Twig_Compiler``) can
+be changed by calling the ``setCompiler()`` method::
+
+    $twig->setCompiler($compiler);
+
+All Twig compilers must implement the ``Twig_CompilerInterface``::
+
+    interface Twig_CompilerInterface
+    {
+        /**
+         * Compiles a node.
+         *
+         * @param  Twig_Node $node The node to compile
+         *
+         * @return Twig_Compiler The current compiler instance
+         */
+        function compile(Twig_Node $node);
+
+        /**
+         * Gets the current PHP code after compilation.
+         *
+         * @return string The PHP code
+         */
+        function getSource();
+    }

+ 18 - 0
lib/twig/doc/index.rst

@@ -0,0 +1,18 @@
+Twig
+====
+
+.. toctree::
+    :maxdepth: 2
+
+    intro
+    templates
+    api
+    advanced
+    extensions
+    hacking
+    recipes
+    coding_standards
+    tags/index
+    filters/index
+    functions/index
+    tests/index

+ 153 - 0
lib/twig/doc/intro.rst

@@ -0,0 +1,153 @@
+Introduction
+============
+
+This is the documentation for Twig, the flexible, fast, and secure template
+engine for PHP.
+
+If you have any exposure to other text-based template languages, such as
+Smarty, Django, or Jinja, you should feel right at home with Twig. It's both
+designer and developer friendly by sticking to PHP's principles and adding
+functionality useful for templating environments.
+
+The key-features are...
+
+* *Fast*: Twig compiles templates down to plain optimized PHP code. The
+  overhead compared to regular PHP code was reduced to the very minimum.
+
+* *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This
+  allows Twig to be used as a template language for applications where users
+  may modify the template design.
+
+* *Flexible*: Twig is powered by a flexible lexer and parser. This allows the
+  developer to define its own custom tags and filters, and create its own DSL.
+
+Prerequisites
+-------------
+
+Twig needs at least **PHP 5.2.4** to run.
+
+Installation
+------------
+
+You have multiple ways to install Twig. If you are unsure what to do, go with
+the tarball.
+
+Installing from the tarball release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Download the most recent tarball from the `download page`_
+2. Unpack the tarball
+3. Move the files somewhere in your project
+
+Installing the development version
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Install Subversion or Git
+2. For Git: ``git clone git://github.com/fabpot/Twig.git``
+3. For Subversion: ``svn co http://svn.twig-project.org/trunk/ twig``
+
+Installing the PEAR package
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Install PEAR
+2. ``pear channel-discover pear.twig-project.org``
+3. ``pear install twig/Twig`` (or ``pear install twig/Twig-beta``)
+
+Installing via Composer
+~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Install composer in your project:
+
+.. code-block:: bash
+
+    curl -s http://getcomposer.org/installer | php``
+
+2. Create a ``composer.json`` file in your project root:
+
+.. code-block:: javascript
+
+    {
+        "require": {
+            "twig/twig": "1.6.0"
+        }
+    }
+
+3. Install via composer
+
+.. code-block:: bash
+
+    php composer.phar install
+
+Installing the C extension
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.4
+    The C extension was added in Twig 1.4.
+
+Twig comes with a C extension that enhances the performance of the Twig
+runtime engine. You can install it like any other PHP extension:
+
+.. code-block:: bash
+
+    $ cd ext/twig
+    $ phpize
+    $ ./configure
+    $ make
+    $ make install
+
+Finally, enable the extension in your ``php.ini`` configuration file:
+
+.. code-block:: ini
+
+    extension=twig.so
+
+And from now on, Twig will automatically compile your templates to take
+advantage of the C extension.
+
+.. tip::
+
+    On Windows, you can also simply download and install a `pre-build DLL`_.
+
+Basic API Usage
+---------------
+
+This section gives you a brief introduction to the PHP API for Twig.
+
+The first step to use Twig is to register its autoloader::
+
+    require_once '/path/to/lib/Twig/Autoloader.php';
+    Twig_Autoloader::register();
+
+Replace the ``/path/to/lib/`` path with the path you used for Twig
+installation.
+
+.. note::
+
+    Twig follows the PEAR convention names for its classes, which means you
+    can easily integrate Twig classes loading in your own autoloader.
+
+.. code-block:: php
+
+    $loader = new Twig_Loader_String();
+    $twig = new Twig_Environment($loader);
+
+    echo $twig->render('Hello {{ name }}!', array('name' => 'Fabien'));
+
+Twig uses a loader (``Twig_Loader_String``) to locate templates, and an
+environment (``Twig_Environment``) to store the configuration.
+
+The ``render()`` method loads the template passed as a first argument and
+renders it with the variables passed as a second argument.
+
+As templates are generally stored on the filesystem, Twig also comes with a
+filesystem loader::
+
+    $loader = new Twig_Loader_Filesystem('/path/to/templates');
+    $twig = new Twig_Environment($loader, array(
+      'cache' => '/path/to/compilation_cache',
+    ));
+
+    echo $twig->render('index.html', array('name' => 'Fabien'));
+
+.. _`download page`: https://github.com/fabpot/Twig/tags
+.. _`pre-build DLL`: https://github.com/stealth35/stealth35.github.com/downloads

+ 301 - 0
lib/twig/doc/recipes.rst

@@ -0,0 +1,301 @@
+Recipes
+=======
+
+Making a Layout conditional
+---------------------------
+
+Working with Ajax means that the same content is sometimes displayed as is,
+and sometimes decorated with a layout. As Twig layout template names can be
+any valid expression, you can pass a variable that evaluates to ``true`` when
+the request is made via Ajax and choose the layout accordingly:
+
+.. code-block:: jinja
+
+    {% extends request.ajax ? "base_ajax.html" : "base.html" %}
+
+    {% block content %}
+        This is the content to be displayed.
+    {% endblock %}
+
+Making an Include dynamic
+-------------------------
+
+When including a template, its name does not need to be a string. For
+instance, the name can depend on the value of a variable:
+
+.. code-block:: jinja
+
+    {% include var ~ '_foo.html' %}
+
+If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be
+rendered.
+
+As a matter of fact, the template name can be any valid expression, such as
+the following:
+
+.. code-block:: jinja
+
+    {% include var|default('index') ~ '_foo.html' %}
+
+Overriding a Template that also extends itself
+----------------------------------------------
+
+A template can be customized in two different ways:
+
+* *Inheritance*: A template *extends* a parent template and overrides some
+  blocks;
+
+* *Replacement*: If you use the filesystem loader, Twig loads the first
+  template it finds in a list of configured directories; a template found in a
+  directory *replaces* another one from a directory further in the list.
+
+But how do you combine both: *replace* a template that also extends itself
+(aka a template in a directory further in the list)?
+
+Let's say that your templates are loaded from both ``.../templates/mysite``
+and ``.../templates/default`` in this order. The ``page.twig`` template,
+stored in ``.../templates/default`` reads as follows:
+
+.. code-block:: jinja
+
+    {# page.twig #}
+    {% extends "layout.twig" %}
+
+    {% block content %}
+    {% endblock %}
+
+You can replace this template by putting a file with the same name in
+``.../templates/mysite``. And if you want to extend the original template, you
+might be tempted to write the following:
+
+.. code-block:: jinja
+
+    {# page.twig in .../templates/mysite #}
+    {% extends "page.twig" %} {# from .../templates/default #}
+
+Of course, this will not work as Twig will always load the template from
+``.../templates/mysite``.
+
+It turns out it is possible to get this to work, by adding a directory right
+at the end of your template directories, which is the parent of all of the
+other directories: ``.../templates`` in our case. This has the effect of
+making every template file within our system uniquely addressable. Most of the
+time you will use the "normal" paths, but in the special case of wanting to
+extend a template with an overriding version of itself we can reference its
+parent's full, unambiguous template path in the extends tag:
+
+.. code-block:: jinja
+
+    {# page.twig in .../templates/mysite #}
+    {% extends "default/page.twig" %} {# from .../templates #}
+
+.. note::
+
+    This recipe was inspired by the following Django wiki page:
+    http://code.djangoproject.com/wiki/ExtendingTemplates
+
+Customizing the Syntax
+----------------------
+
+Twig allows some syntax customization for the block delimiters. It's not
+recommended to use this feature as templates will be tied with your custom
+syntax. But for specific projects, it can make sense to change the defaults.
+
+To change the block delimiters, you need to create your own lexer object::
+
+    $twig = new Twig_Environment();
+
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'  => array('{#', '#}'),
+        'tag_block'    => array('{%', '%}'),
+        'tag_variable' => array('{{', '}}'),
+    ));
+    $twig->setLexer($lexer);
+
+Here are some configuration example that simulates some other template engines
+syntax::
+
+    // Ruby erb syntax
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'  => array('<%#', '%>'),
+        'tag_block'    => array('<%', '%>'),
+        'tag_variable' => array('<%=', '%>'),
+    ));
+
+    // SGML Comment Syntax
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'  => array('<!--#', '-->'),
+        'tag_block'    => array('<!--', '-->'),
+        'tag_variable' => array('${', '}'),
+    ));
+
+    // Smarty like
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'  => array('{*', '*}'),
+        'tag_block'    => array('{', '}'),
+        'tag_variable' => array('{$', '}'),
+    ));
+
+Using dynamic Object Properties
+-------------------------------
+
+When Twig encounters a variable like ``article.title``, it tries to find a
+``title`` public property in the ``article`` object.
+
+It also works if the property does not exist but is rather defined dynamically
+thanks to the magic ``__get()`` method; you just need to also implement the
+``__isset()`` magic method like shown in the following snippet of code::
+
+    class Article
+    {
+        public function __get($name)
+        {
+            if ('title' == $name)
+            {
+                return 'The title';
+            }
+
+            // throw some kind of error
+        }
+
+        public function __isset($name)
+        {
+            if ('title' == $name)
+            {
+                return true;
+            }
+
+            return false;
+        }
+    }
+
+Accessing the parent Context in Nested Loops
+--------------------------------------------
+
+Sometimes, when using nested loops, you need to access the parent context. The
+parent context is always accessible via the ``loop.parent`` variable. For
+instance, if you have the following template data::
+
+    $data = array(
+        'topics' => array(
+            'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'),
+            'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'),
+        ),
+    );
+
+And the following template to display all messages in all topics:
+
+.. code-block:: jinja
+
+    {% for topic, messages in topics %}
+        * {{ loop.index }}: {{ topic }}
+      {% for message in messages %}
+          - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
+      {% endfor %}
+    {% endfor %}
+
+The output will be similar to:
+
+.. code-block:: text
+
+    * 1: topic1
+      - 1.1: The message 1 of topic 1
+      - 1.2: The message 2 of topic 1
+    * 2: topic2
+      - 2.1: The message 1 of topic 2
+      - 2.2: The message 2 of topic 2
+
+In the inner loop, the ``loop.parent`` variable is used to access the outer
+context. So, the index of the current ``topic`` defined in the outer for loop
+is accessible via the ``loop.parent.loop.index`` variable.
+
+Defining undefined Functions and Filters on the Fly
+---------------------------------------------------
+
+When a function (or a filter) is not defined, Twig defaults to throw a
+``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any
+valid PHP callable) which should return a function (or a filter).
+
+For filters, register callbacks with ``registerUndefinedFilterCallback()``.
+For functions, use ``registerUndefinedFunctionCallback()``::
+
+    // auto-register all native PHP functions as Twig functions
+    // don't try this at home as it's not secure at all!
+    $twig->registerUndefinedFunctionCallback(function ($name) {
+        if (function_exists($name)) {
+            return new Twig_Function_Function($name);
+        }
+
+        return false;
+    });
+
+If the callable is not able to return a valid function (or filter), it must
+return ``false``.
+
+If you register more than one callback, Twig will call them in turn until one
+does not return ``false``.
+
+.. tip::
+
+    As the resolution of functions and filters is done during compilation,
+    there is no overhead when registering these callbacks.
+
+Validating the Template Syntax
+------------------------------
+
+When template code is providing by a third-party (through a web interface for
+instance), it might be interesting to validate the template syntax before
+saving it. If the template code is stored in a `$template` variable, here is
+how you can do it::
+
+    try {
+        $twig->parse($twig->tokenize($template));
+
+        // the $template is valid
+    } catch (Twig_Error_Syntax $e) {
+        // $template contains one or more syntax errors
+    }
+
+Refreshing modified Templates when APC is enabled and apc.stat = 0
+------------------------------------------------------------------
+
+When using APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing
+the template cache won't update the APC cache. To get around this, one can
+extend ``Twig_Environment`` and force the update of the APC cache when Twig
+rewrites the cache::
+
+    class Twig_Environment_APC extends Twig_Environment
+    {
+        protected function writeCacheFile($file, $content)
+        {
+            parent::writeCacheFile($file, $content);
+
+            // Compile cached file into bytecode cache
+            apc_compile_file($file);
+        }
+    }
+
+Reusing a stateful Node Visitor
+-------------------------------
+
+When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to
+visit *all* templates it compiles. If you need to keep some state information
+around, you probably want to reset it when visiting a new template.
+
+This can be easily achieved with the following code::
+
+    protected $someTemplateState = array();
+
+    public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
+    {
+        if ($node instanceof Twig_Node_Module) {
+            // reset the state as we are entering a new template
+            $this->someTemplateState = array();
+        }
+
+        // ...
+
+        return $node;
+    }
+
+.. _callback: http://www.php.net/manual/en/function.is-callable.php

+ 43 - 0
lib/twig/doc/tags/autoescape.rst

@@ -0,0 +1,43 @@
+``autoescape``
+==============
+
+Whether automatic escaping is enabled or not, you can mark a section of a
+template to be escaped or not by using the ``autoescape`` tag:
+
+.. code-block:: jinja
+
+    {% autoescape true %}
+        Everything will be automatically escaped in this block
+    {% endautoescape %}
+
+    {% autoescape false %}
+        Everything will be outputed as is in this block
+    {% endautoescape %}
+
+    {% autoescape true js %}
+        Everything will be automatically escaped in this block
+        using the js escaping strategy
+    {% endautoescape %}
+
+When automatic escaping is enabled everything is escaped by default except for
+values explicitly marked as safe. Those can be marked in the template by using
+the :doc:`raw<../filters/raw>` filter:
+
+.. code-block:: jinja
+
+    {% autoescape true %}
+        {{ safe_value|raw }}
+    {% endautoescape %}
+
+Functions returning template data (like :doc:`macros<macro>` and
+:doc:`parent<../functions/parent>`) always return safe markup.
+
+.. note::
+
+    Twig is smart enough to not escape an already escaped value by the
+    :doc:`escape<../filters/escape>` filter.
+
+.. note::
+
+    The chapter :doc:`Twig for Developers<../api>` gives more information
+    about when and how automatic escaping is applied.

+ 11 - 0
lib/twig/doc/tags/block.rst

@@ -0,0 +1,11 @@
+``block``
+=========
+
+Blocks are used for inheritance and act as placeholders and replacements at
+the same time. They are documented in detail in the documentation for the
+:doc:`extends<../tags/extends>` tag.
+
+Block names should consist of alphanumeric characters, and underscores. Dashes
+are not permitted.
+
+.. seealso:: :doc:`block<../functions/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`, :doc:`extends<../tags/extends>`

+ 12 - 0
lib/twig/doc/tags/do.rst

@@ -0,0 +1,12 @@
+``do``
+======
+
+.. versionadded:: 1.5
+    The do tag was added in Twig 1.5.
+
+The ``do`` tag works exactly like the regular variable expression (``{{ ...
+}}``) just that it doesn't print anything:
+
+.. code-block:: jinja
+
+    {% do 1 + 2 %}

+ 187 - 0
lib/twig/doc/tags/extends.rst

@@ -0,0 +1,187 @@
+``extends``
+===========
+
+The ``extends`` tag can be used to extend a template from another one.
+
+.. note::
+
+    Like PHP, Twig does not support multiple inheritance. So you can only have
+    one extends tag called per rendering. However, Twig supports horizontal
+    :doc:`reuse<use>`.
+
+Let's define a base template, ``base.html``, which defines a simple HTML
+skeleton document:
+
+.. code-block:: html+jinja
+
+    <!DOCTYPE html>
+    <html>
+        <head>
+            {% block head %}
+                <link rel="stylesheet" href="style.css" />
+                <title>{% block title %}{% endblock %} - My Webpage</title>
+            {% endblock %}
+        </head>
+        <body>
+            <div id="content">{% block content %}{% endblock %}</div>
+            <div id="footer">
+                {% block footer %}
+                    &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
+                {% endblock %}
+            </div>
+        </body>
+    </html>
+
+In this example, the :doc:`block<block>` tags define four blocks that child
+templates can fill in. All the ``block`` tag does is to tell the template
+engine that a child template may override those portions of the template.
+
+Child Template
+--------------
+
+A child template might look like this:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block title %}Index{% endblock %}
+    {% block head %}
+        {{ parent() }}
+        <style type="text/css">
+            .important { color: #336699; }
+        </style>
+    {% endblock %}
+    {% block content %}
+        <h1>Index</h1>
+        <p class="important">
+            Welcome on my awesome homepage.
+        </p>
+    {% endblock %}
+
+The ``extends`` tag is the key here. It tells the template engine that this
+template "extends" another template. When the template system evaluates this
+template, first it locates the parent. The extends tag should be the first tag
+in the template.
+
+Note that since the child template doesn't define the ``footer`` block, the
+value from the parent template is used instead.
+
+You can't define multiple ``block`` tags with the same name in the same
+template. This limitation exists because a block tag works in "both"
+directions. That is, a block tag doesn't just provide a hole to fill - it also
+defines the content that fills the hole in the *parent*. If there were two
+similarly-named ``block`` tags in a template, that template's parent wouldn't
+know which one of the blocks' content to use.
+
+If you want to print a block multiple times you can however use the
+``block`` function:
+
+.. code-block:: jinja
+
+    <title>{% block title %}{% endblock %}</title>
+    <h1>{{ block('title') }}</h1>
+    {% block body %}{% endblock %}
+
+Parent Blocks
+-------------
+
+It's possible to render the contents of the parent block by using the
+:doc:`parent<../functions/parent>` function. This gives back the results of
+the parent block:
+
+.. code-block:: jinja
+
+    {% block sidebar %}
+        <h3>Table Of Contents</h3>
+        ...
+        {{ parent() }}
+    {% endblock %}
+
+Named Block End-Tags
+--------------------
+
+Twig allows you to put the name of the block after the end tag for better
+readability:
+
+.. code-block:: jinja
+
+    {% block sidebar %}
+        {% block inner_sidebar %}
+            ...
+        {% endblock inner_sidebar %}
+    {% endblock sidebar %}
+
+Of course, the name after the ``endblock`` word must match the block name.
+
+Block Nesting and Scope
+-----------------------
+
+Blocks can be nested for more complex layouts. Per default, blocks have access
+to variables from outer scopes:
+
+.. code-block:: jinja
+
+    {% for item in seq %}
+        <li>{% block loop_item %}{{ item }}{% endblock %}</li>
+    {% endfor %}
+
+Block Shortcuts
+---------------
+
+For blocks with few content, it's possible to use a shortcut syntax. The
+following constructs do the same:
+
+.. code-block:: jinja
+
+    {% block title %}
+        {{ page_title|title }}
+    {% endblock %}
+
+.. code-block:: jinja
+
+    {% block title page_title|title %}
+
+Dynamic Inheritance
+-------------------
+
+Twig supports dynamic inheritance by using a variable as the base template:
+
+.. code-block:: jinja
+
+    {% extends some_var %}
+
+If the variable evaluates to a ``Twig_Template`` object, Twig will use it as
+the parent template::
+
+    // {% extends layout %}
+
+    $layout = $twig->loadTemplate('some_layout_template.twig');
+
+    $twig->display('template.twig', array('layout' => $layout));
+
+.. versionadded:: 1.2
+    The possibility to pass an array of templates has been added in Twig 1.2.
+
+You can also provide a list of templates that are checked for existence. The
+first template that exists will be used as a parent:
+
+.. code-block:: jinja
+
+    {% extends ['layout.html', 'base_layout.html'] %}
+
+Conditional Inheritance
+-----------------------
+
+As the template name for the parent can be any valid Twig expression, it's
+possible to make the inheritance mechanism conditional:
+
+.. code-block:: jinja
+
+    {% extends standalone ? "minimum.html" : "base.html" %}
+
+In this example, the template will extend the "minimum.html" layout template
+if the ``standalone`` variable evaluates to ``true``, and "base.html"
+otherwise.
+
+.. seealso:: :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`

+ 21 - 0
lib/twig/doc/tags/filter.rst

@@ -0,0 +1,21 @@
+``filter``
+==========
+
+Filter sections allow you to apply regular Twig filters on a block of template
+data. Just wrap the code in the special ``filter`` section:
+
+.. code-block:: jinja
+
+    {% filter upper %}
+        This text becomes uppercase
+    {% endfilter %}
+
+You can also chain filters:
+
+.. code-block:: jinja
+
+    {% filter lower|escape %}
+        <strong>SOME TEXT</strong>
+    {% endfilter %}
+
+    {# outputs "&lt;strong&gt;some text&lt;/strong&gt;" #}

+ 17 - 0
lib/twig/doc/tags/flush.rst

@@ -0,0 +1,17 @@
+``flush``
+=========
+
+.. versionadded:: 1.5
+    The flush tag was added in Twig 1.5.
+
+The ``flush`` tag tells Twig to flush the output buffer:
+
+.. code-block:: jinja
+
+    {% flush %}
+
+.. note::
+
+    Internally, Twig uses the PHP `flush`_ function.
+
+.. _`flush`: http://php.net/flush

+ 149 - 0
lib/twig/doc/tags/for.rst

@@ -0,0 +1,149 @@
+``for``
+=======
+
+Loop over each item in a sequence. For example, to display a list of users
+provided in a variable called ``users``:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for user in users %}
+            <li>{{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
+
+.. note::
+
+    A sequence can be either an array or an object implementing the
+    ``Traversable`` interface.
+
+If you do need to iterate over a sequence of numbers, you can use the ``..``
+operator:
+
+.. code-block:: jinja
+
+    {% for i in 0..10 %}
+        * {{ i }}
+    {% endfor %}
+
+The above snippet of code would print all numbers from 0 to 10.
+
+It can be also useful with letters:
+
+.. code-block:: jinja
+
+    {% for letter in 'a'..'z' %}
+        * {{ letter }}
+    {% endfor %}
+
+The ``..`` operator can take any expression at both sides:
+
+.. code-block:: jinja
+
+    {% for letter in 'a'|upper..'z'|upper %}
+        * {{ letter }}
+    {% endfor %}
+
+.. tip:
+
+    If you need a step different from 1, you can use the ``range`` function
+    instead.
+
+The `loop` variable
+-------------------
+
+Inside of a ``for`` loop block you can access some special variables:
+
+===================== =============================================================
+Variable              Description
+===================== =============================================================
+``loop.index``        The current iteration of the loop. (1 indexed)
+``loop.index0``       The current iteration of the loop. (0 indexed)
+``loop.revindex``     The number of iterations from the end of the loop (1 indexed)
+``loop.revindex0``    The number of iterations from the end of the loop (0 indexed)
+``loop.first``        True if first iteration
+``loop.last``         True if last iteration
+``loop.length``       The number of items in the sequence
+``loop.parent``       The parent context
+===================== =============================================================
+
+.. note::
+
+    The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and
+    ``loop.last`` variables are only available for PHP arrays, or objects that
+    implement the ``Countable`` interface.
+
+.. versionadded:: 1.2
+    The ``if`` modifier support has been added in Twig 1.2.
+
+Adding a condition
+------------------
+
+Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You
+can however filter the sequence during iteration which allows you to skip
+items. The following example skips all the users which are not active:
+
+.. code-block:: jinja
+
+    <ul>
+        {% for user in users if user.active %}
+            <li>{{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
+
+The advantage is that the special loop variable will count correctly thus not
+counting the users not iterated over.
+
+.. note::
+
+    Using the ``loop`` variable within the condition is not recommended as it
+    will probably not be doing what you expect it to. For instance, adding a
+    condition like ``loop.index > 4`` won't work as the index is only
+    incremented when the condition is true (so the condition will never
+    match).
+
+The `else` Clause
+-----------------
+
+If no iteration took place because the sequence was empty, you can render a
+replacement block by using ``else``:
+
+.. code-block:: jinja
+
+    <ul>
+        {% for user in users %}
+            <li>{{ user.username|e }}</li>
+        {% else %}
+            <li><em>no user found</em></li>
+        {% endfor %}
+    </ul>
+
+Iterating over Keys
+-------------------
+
+By default, a loop iterates over the values of the sequence. You can iterate
+on keys by using the ``keys`` filter:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for key in users|keys %}
+            <li>{{ key }}</li>
+        {% endfor %}
+    </ul>
+
+Iterating over Keys and Values
+------------------------------
+
+You can also access both keys and values:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for key, user in users %}
+            <li>{{ key }}: {{ user.username|e }}</li>
+        {% endfor %}
+    </ul>

+ 8 - 0
lib/twig/doc/tags/from.rst

@@ -0,0 +1,8 @@
+``from``
+========
+
+The ``from`` tags import :doc:`macro<../tags/macro>` names into the current
+namespace. The tag is documented in detail in the documentation for the
+:doc:`import<../tags/import>` tag.
+
+.. seealso:: :doc:`macro<../tags/macro>`, :doc:`import<../tags/import>`

+ 43 - 0
lib/twig/doc/tags/if.rst

@@ -0,0 +1,43 @@
+``if``
+======
+
+The ``if`` statement in Twig is comparable with the if statements of PHP.
+
+In the simplest form you can use it to test if an expression evaluates to
+``true``:
+
+.. code-block:: jinja
+
+    {% if online == false %}
+        <p>Our website is in maintenance mode. Please, come back later.</p>
+    {% endif %}
+
+You can also test if an array is not empty:
+
+.. code-block:: jinja
+
+    {% if users %}
+        <ul>
+            {% for user in users %}
+                <li>{{ user.username|e }}</li>
+            {% endfor %}
+        </ul>
+    {% endif %}
+
+.. note::
+
+    If you want to test if the variable is defined, use ``if users is
+    defined`` instead.
+
+For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can use
+more complex ``expressions`` there too:
+
+.. code-block:: jinja
+
+    {% if kenny.sick %}
+        Kenny is sick.
+    {% elseif kenny.dead %}
+        You killed Kenny!  You bastard!!!
+    {% else %}
+        Kenny looks okay --- so far
+    {% endif %}

+ 79 - 0
lib/twig/doc/tags/import.rst

@@ -0,0 +1,79 @@
+``import``
+==========
+
+Twig supports putting often used code into :doc:`macros<../tags/macro>`. These
+macros can go into different templates and get imported from there.
+
+There are two ways to import templates. You can import the complete template
+into a variable or request specific macros from it.
+
+Imagine we have a helper module that renders forms (called ``forms.html``):
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+    {% macro textarea(name, value, rows) %}
+        <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+    {% endmacro %}
+
+The easiest and most flexible is importing the whole module into a variable.
+That way you can access the attributes:
+
+.. code-block:: jinja
+
+    {% import 'forms.html' as forms %}
+
+    <dl>
+        <dt>Username</dt>
+        <dd>{{ forms.input('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ forms.input('password', null, 'password') }}</dd>
+    </dl>
+    <p>{{ forms.textarea('comment') }}</p>
+
+Alternatively you can import names from the template into the current
+namespace:
+
+.. code-block:: jinja
+
+    {% from 'forms.html' import input as input_field, textarea %}
+
+    <dl>
+        <dt>Username</dt>
+        <dd>{{ input_field('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ input_field('password', '', 'password') }}</dd>
+    </dl>
+    <p>{{ textarea('comment') }}</p>
+
+Importing is not needed if the macros and the template are defined in the same
+file; use the special ``_self`` variable instead:
+
+.. code-block:: jinja
+
+    {# index.html template #}
+
+    {% macro textarea(name, value, rows) %}
+        <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+    {% endmacro %}
+
+    <p>{{ _self.textarea('comment') }}</p>
+
+But you can still create an alias by importing from the ``_self`` variable:
+
+.. code-block:: jinja
+
+    {# index.html template #}
+
+    {% macro textarea(name, value, rows) %}
+        <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+    {% endmacro %}
+
+    {% import _self as forms %}
+
+    <p>{{ forms.textarea('comment') }}</p>
+
+.. seealso:: :doc:`macro<../tags/macro>`, :doc:`from<../tags/from>`

+ 86 - 0
lib/twig/doc/tags/include.rst

@@ -0,0 +1,86 @@
+``include``
+===========
+
+The ``include`` statement includes a template and return the rendered content
+of that file into the current namespace:
+
+.. code-block:: jinja
+
+    {% include 'header.html' %}
+        Body
+    {% include 'footer.html' %}
+
+Included templates have access to the variables of the active context.
+
+If you are using the filesystem loader, the templates are looked for in the
+paths defined by it.
+
+You can add additional variables by passing them after the ``with`` keyword:
+
+.. code-block:: jinja
+
+    {# the foo template will have access to the variables from the current context and the foo one #}
+    {% include 'foo' with {'foo': 'bar'} %}
+
+    {% set vars = {'foo': 'bar'} %}
+    {% include 'foo' with vars %}
+
+You can disable access to the context by appending the ``only`` keyword:
+
+.. code-block:: jinja
+
+    {# only the foo variable will be accessible #}
+    {% include 'foo' with {'foo': 'bar'} only %}
+
+.. code-block:: jinja
+
+    {# no variable will be accessible #}
+    {% include 'foo' only %}
+
+.. tip::
+
+    When including a template created by an end user, you should consider
+    sandboxing it. More information in the :doc:`Twig for Developers<../api>`
+    chapter.
+
+The template name can be any valid Twig expression:
+
+.. code-block:: jinja
+
+    {% include some_var %}
+    {% include ajax ? 'ajax.html' : 'not_ajax.html' %}
+
+And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
+directly::
+
+    // {% include template %}
+
+    $template = $twig->loadTemplate('some_template.twig');
+
+    $twig->loadTemplate('template.twig')->display(array('template' => $template));
+
+.. versionadded:: 1.2
+    The ``ignore missing`` feature has been added in Twig 1.2.
+
+You can mark an include with ``ignore missing`` in which case Twig will ignore
+the statement if the template to be ignored does not exist. It has to be
+placed just after the template name. Here some valid examples:
+
+.. code-block:: jinja
+
+    {% include "sidebar.html" ignore missing %}
+    {% include "sidebar.html" ignore missing with {'foo': 'bar} %}
+    {% include "sidebar.html" ignore missing only %}
+
+.. versionadded:: 1.2
+    The possibility to pass an array of templates has been added in Twig 1.2.
+
+You can also provide a list of templates that are checked for existence before
+inclusion. The first template that exists will be included:
+
+.. code-block:: jinja
+
+    {% include ['page_detailed.html', 'page.html'] %}
+
+If ``ignore missing`` is given, it will fall back to rendering nothing if none
+of the templates exist, otherwise it will throw an exception.

+ 22 - 0
lib/twig/doc/tags/index.rst

@@ -0,0 +1,22 @@
+Tags
+====
+
+.. toctree::
+    :maxdepth: 1
+
+    for
+    if
+    macro
+    filter
+    set
+    extends
+    block
+    include
+    import
+    from
+    use
+    spaceless
+    autoescape
+    raw
+    flush
+    do

+ 91 - 0
lib/twig/doc/tags/macro.rst

@@ -0,0 +1,91 @@
+``macro``
+=========
+
+Macros are comparable with functions in regular programming languages. They
+are useful to put often used HTML idioms into reusable elements to not repeat
+yourself.
+
+Here is a small example of a macro that renders a form element:
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+Macros differs from native PHP functions in a few ways:
+
+* Default argument values are defined by using the ``default`` filter in the
+  macro body;
+
+* Arguments of a macro are always optional.
+
+But as PHP functions, macros don't have access to the current template
+variables.
+
+.. tip::
+
+    You can pass the whole context as an argument by using the special
+    ``_context`` variable.
+
+Macros can be defined in any template, and need to be "imported" before being
+used (see the documentation for the :doc:`import<../tags/import>` tag for more
+information):
+
+.. code-block:: jinja
+
+    {% import "forms.html" as forms %}
+
+The above ``import`` call imports the "forms.html" file (which can contain only
+macros, or a template and some macros), and import the functions as items of
+the ``forms`` variable.
+
+The macro can then be called at will:
+
+.. code-block:: jinja
+
+    <p>{{ forms.input('username') }}</p>
+    <p>{{ forms.input('password', null, 'password') }}</p>
+
+If macros are defined and used in the same template, you can use the
+special ``_self`` variable, without importing them:
+
+.. code-block:: jinja
+
+    <p>{{ _self.input('username') }}</p>
+
+When you want to use a macro in another one from the same file, use the ``_self``
+variable:
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+      <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+    {% macro wrapped_input(name, value, type, size) %}
+        <div class="field">
+            {{ _self.input(name, value, type, size) }}
+        </div>
+    {% endmacro %}
+
+When the macro is defined in another file, you need to import it:
+
+.. code-block:: jinja
+
+    {# forms.html #}
+
+    {% macro input(name, value, type, size) %}
+      <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+    {# shortcuts.html #}
+
+    {% macro wrapped_input(name, value, type, size) %}
+        {% import "forms.html" as forms %}
+        <div class="field">
+            {{ forms.input(name, value, type, size) }}
+        </div>
+    {% endmacro %}
+
+.. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>`

+ 16 - 0
lib/twig/doc/tags/raw.rst

@@ -0,0 +1,16 @@
+``raw``
+=======
+
+The ``raw`` tag marks sections as being raw text that should not be parsed.
+For example to put Twig syntax as example into a template you can use this
+snippet:
+
+.. code-block:: jinja
+
+    {% raw %}
+        <ul>
+        {% for item in seq %}
+            <li>{{ item }}</li>
+        {% endfor %}
+        </ul>
+    {% endraw %}

+ 32 - 0
lib/twig/doc/tags/set.rst

@@ -0,0 +1,32 @@
+``set``
+=======
+
+Inside code blocks you can also assign values to variables. Assignments use
+the ``set`` tag and can have multiple targets:
+
+.. code-block:: jinja
+
+    {% set foo = 'foo' %}
+
+    {% set foo = [1, 2] %}
+
+    {% set foo = {'foo': 'bar'} %}
+
+    {% set foo = 'foo' ~ 'bar' %}
+
+    {% set foo, bar = 'foo', 'bar' %}
+
+The ``set`` tag can also be used to 'capture' chunks of text:
+
+.. code-block:: jinja
+
+    {% set foo %}
+      <div id="pagination">
+        ...
+      </div>
+    {% endset %}
+
+.. caution::
+
+    If you enable automatic output escaping, Twig will only consider the
+    content to be safe when capturing chunks of text.

+ 37 - 0
lib/twig/doc/tags/spaceless.rst

@@ -0,0 +1,37 @@
+``spaceless``
+=============
+
+Use the ``spaceless`` tag to remove whitespace *between HTML tags*, not
+whitespace within HTML tags or whitespace in plain text:
+
+.. code-block:: jinja
+
+    {% spaceless %}
+        <div>
+            <strong>foo</strong>
+        </div>
+    {% endspaceless %}
+
+    {# output will be <div><strong>foo</strong></div> #}
+
+This tag is not meant to "optimize" the size of the generated HTML content but
+merely to avoid extra whitespace between HTML tags to avoid browser rendering
+quirks under some circumstances.
+
+.. tip::
+
+    If you want to optimize the size of the generated HTML content, gzip
+    compress the output instead.
+
+.. tip::
+
+    If you want to create a tag that actually removes all extra whitespace in
+    an HTML string, be warned that this is not as easy as it seems to be
+    (think of ``textarea`` or ``pre`` tags for instance). Using a third-party
+    library like Tidy is probably a better idea.
+
+.. tip::
+
+    For more information on whitespace control, read the
+    :doc:`dedicated<../templates>` section of the documentation and learn how
+    you can also use the whitespace control modifier on your tags.

+ 123 - 0
lib/twig/doc/tags/use.rst

@@ -0,0 +1,123 @@
+``use``
+=======
+
+.. versionadded:: 1.1
+    Horizontal reuse was added in Twig 1.1.
+
+.. note::
+
+    Horizontal reuse is an advanced Twig feature that is hardly ever needed in
+    regular templates. It is mainly used by projects that need to make
+    template blocks reusable without using inheritance.
+
+Template inheritance is one of the most powerful Twig's feature but it is
+limited to single inheritance; a template can only extend one other template.
+This limitation makes template inheritance simple to understand and easy to
+debug:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+Horizontal reuse is a way to achieve the same goal as multiple inheritance,
+but without the associated complexity:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% use "blocks.html" %}
+
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+The ``use`` statement tells Twig to import the blocks defined in
+```blocks.html`` into the current template (it's like macros, but for blocks):
+
+.. code-block:: jinja
+
+    # blocks.html
+    {% block sidebar %}{% endblock %}
+
+In this example, the ``use`` statement imports the ``sidebar`` block into the
+main template. The code is mostly equivalent to the following one (the
+imported blocks are not outputted automatically):
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block sidebar %}{% endblock %}
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+.. note::
+
+    The ``use`` tag only imports a template if it does not extend another
+    template, if it does not define macros, and if the body is empty. But it
+    can *use* other templates.
+
+.. note::
+
+    Because ``use`` statements are resolved independently of the context
+    passed to the template, the template reference cannot be an expression.
+
+The main template can also override any imported block. If the template
+already defines the ``sidebar`` block, then the one defined in ``blocks.html``
+is ignored. To avoid name conflicts, you can rename imported blocks:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% use "blocks.html" with sidebar as base_sidebar %}
+
+    {% block sidebar %}{% endblock %}
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+.. versionadded:: 1.3
+    The ``parent()`` support was added in Twig 1.3.
+
+The ``parent()`` function automatically determines the correct inheritance
+tree, so it can be used when overriding a block defined in an imported
+template:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% use "blocks.html" %}
+
+    {% block sidebar %}
+        {{ parent() }}
+    {% endblock %}
+
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+In this example, ``parent()`` will correctly call the ``sidebar`` block from
+the ``blocks.html`` template.
+
+.. tip::
+
+    In Twig 1.2, renaming allows you to simulate inheritance by calling the
+    "parent" block:
+
+    .. code-block:: jinja
+
+        {% extends "base.html" %}
+
+        {% use "blocks.html" with sidebar as parent_sidebar %}
+
+        {% block sidebar %}
+            {{ block('parent_sidebar') }}
+        {% endblock %}
+
+.. note::
+
+    You can use as many ``use`` statements as you want in any given template.
+    If two imported templates define the same block, the latest one wins.

+ 719 - 0
lib/twig/doc/templates.rst

@@ -0,0 +1,719 @@
+Twig for Template Designers
+===========================
+
+This document describes the syntax and semantics of the template engine and
+will be most useful as reference to those creating Twig templates.
+
+Synopsis
+--------
+
+A template is simply a text file. It can generate any text-based format (HTML,
+XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or
+``.xml`` are just fine.
+
+A template contains **variables** or **expressions**, which get replaced with
+values when the template is evaluated, and **tags**, which control the logic
+of the template.
+
+Below is a minimal template that illustrates a few basics. We will cover the
+details later on:
+
+.. code-block:: html+jinja
+
+    <!DOCTYPE html>
+    <html>
+        <head>
+            <title>My Webpage</title>
+        </head>
+        <body>
+            <ul id="navigation">
+            {% for item in navigation %}
+                <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
+            {% endfor %}
+            </ul>
+
+            <h1>My Webpage</h1>
+            {{ a_variable }}
+        </body>
+    </html>
+
+There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first
+one is used to execute statements such as for-loops, the latter prints the
+result of an expression to the template.
+
+IDEs Integration
+----------------
+
+Many IDEs support syntax highlighting and auto-completion for Twig:
+
+* *Textmate* via the `Twig bundle`_
+* *Vim* via the `Jinja syntax plugin`_
+* *Netbeans* via the `Twig syntax plugin`_
+* *PhpStorm* (native as of 2.1)
+* *Eclipse* via the `Twig plugin`_
+* *Sublime Text* via the `Twig bundle`_
+* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects)
+* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_
+
+Variables
+---------
+
+The application passes variables to the templates you can mess around in the
+template. Variables may have attributes or elements on them you can access
+too. How a variable looks like heavily depends on the application providing
+those.
+
+You can use a dot (``.``) to access attributes of a variable (methods or
+properties of a PHP object, or items of a PHP array), or the so-called
+"subscript" syntax (``[]``):
+
+.. code-block:: jinja
+
+    {{ foo.bar }}
+    {{ foo['bar'] }}
+
+.. note::
+
+    It's important to know that the curly braces are *not* part of the
+    variable but the print statement. If you access variables inside tags
+    don't put the braces around.
+
+If a variable or attribute does not exist you will get back a ``null`` value.
+
+.. sidebar:: Implementation
+
+    For convenience sake ``foo.bar`` does the following things on the PHP
+    layer:
+
+    * check if ``foo`` is an array and ``bar`` a valid element;
+    * if not, and if ``foo`` is an object, check that ``bar`` is a valid property;
+    * if not, and if ``foo`` is an object, check that ``bar`` is a valid method
+      (even if ``bar`` is the constructor - use ``__construct()`` instead);
+    * if not, and if ``foo`` is an object, check that ``getBar`` is a valid method;
+    * if not, and if ``foo`` is an object, check that ``isBar`` is a valid method;
+    * if not, return a ``null`` value.
+
+    ``foo['bar']`` on the other hand only works with PHP arrays:
+
+    * check if ``foo`` is an array and ``bar`` a valid element;
+    * if not, return a ``null`` value.
+
+.. note::
+
+    If you want to get a dynamic attribute on a variable, use the
+    :doc:`attribute<functions/attribute>` function instead.
+
+Global Variables
+~~~~~~~~~~~~~~~~
+
+The following variables are always available in templates:
+
+* ``_self``: references the current template;
+* ``_context``: references the current context;
+* ``_charset``: references the current charset.
+
+Setting Variables
+~~~~~~~~~~~~~~~~~
+
+You can assign values to variables inside code blocks. Assignments use the
+:doc:`set<tags/set>` tag:
+
+.. code-block:: jinja
+
+    {% set foo = 'foo' %}
+    {% set foo = [1, 2] %}
+    {% set foo = {'foo': 'bar'} %}
+
+Filters
+-------
+
+Variables can be modified by **filters**. Filters are separated from the
+variable by a pipe symbol (``|``) and may have optional arguments in
+parentheses. Multiple filters can be chained. The output of one filter is
+applied to the next.
+
+The following example removes all HTML tags from the ``name`` and title-cases
+it:
+
+.. code-block:: jinja
+
+    {{ name|striptags|title }}
+
+Filters that accept arguments have parentheses around the arguments. This
+example will join a list by commas:
+
+.. code-block:: jinja
+
+    {{ list|join(', ') }}
+
+To apply a filter on a section of code, wrap it with the
+:doc:`filter<tags/filter>` tag:
+
+.. code-block:: jinja
+
+    {% filter upper %}
+      This text becomes uppercase
+    {% endfilter %}
+
+Go to the :doc:`filters<filters/index>` page to learn more about the built-in
+filters.
+
+Functions
+---------
+
+Functions can be called to generate content. Functions are called by their
+name followed by parentheses (``()``) and may have arguments.
+
+For instance, the ``range`` function returns a list containing an arithmetic
+progression of integers:
+
+.. code-block:: jinja
+
+    {% for i in range(0, 3) %}
+        {{ i }},
+    {% endfor %}
+
+Go to the :doc:`functions<functions/index>` page to learn more about the
+built-in functions.
+
+Control Structure
+-----------------
+
+A control structure refers to all those things that control the flow of a
+program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as
+well as things like blocks. Control structures appear inside ``{% ... %}``
+blocks.
+
+For example, to display a list of users provided in a variable called
+``users``, use the :doc:`for<tags/for>` tag:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for user in users %}
+            <li>{{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
+
+The :doc:`if<tags/if>` tag can be used to test an expression:
+
+.. code-block:: jinja
+
+    {% if users|length > 0 %}
+        <ul>
+            {% for user in users %}
+                <li>{{ user.username|e }}</li>
+            {% endfor %}
+        </ul>
+    {% endif %}
+
+Go to the :doc:`tags<tags/index>` page to learn more about the built-in tags.
+
+Comments
+--------
+
+To comment-out part of a line in a template, use the comment syntax ``{# ...
+#}``. This is useful for debugging or to add information for other template
+designers or yourself:
+
+.. code-block:: jinja
+
+    {# note: disabled template because we no longer use this
+        {% for user in users %}
+            ...
+        {% endfor %}
+    #}
+
+Including other Templates
+-------------------------
+
+The :doc:`include<tags/include>` tag is useful to include a template and
+return the rendered content of that template into the current one:
+
+.. code-block:: jinja
+
+    {% include 'sidebar.html' %}
+
+Per default included templates are passed the current context.
+
+The context that is passed to the included template includes variables defined
+in the template:
+
+.. code-block:: jinja
+
+    {% for box in boxes %}
+        {% include "render_box.html" %}
+    {% endfor %}
+
+The included template ``render_box.html`` is able to access ``box``.
+
+The filename of the template depends on the template loader. For instance, the
+``Twig_Loader_Filesystem`` allows you to access other templates by giving the
+filename. You can access templates in subdirectories with a slash:
+
+.. code-block:: jinja
+
+    {% include "sections/articles/sidebar.html" %}
+
+This behavior depends on the application embedding Twig.
+
+Template Inheritance
+--------------------
+
+The most powerful part of Twig is template inheritance. Template inheritance
+allows you to build a base "skeleton" template that contains all the common
+elements of your site and defines **blocks** that child templates can
+override.
+
+Sounds complicated but is very basic. It's easiest to understand it by
+starting with an example.
+
+Let's define a base template, ``base.html``, which defines a simple HTML
+skeleton document that you might use for a simple two-column page:
+
+.. code-block:: html+jinja
+
+    <!DOCTYPE html>
+    <html>
+        <head>
+            {% block head %}
+                <link rel="stylesheet" href="style.css" />
+                <title>{% block title %}{% endblock %} - My Webpage</title>
+            {% endblock %}
+        </head>
+        <body>
+            <div id="content">{% block content %}{% endblock %}</div>
+            <div id="footer">
+                {% block footer %}
+                    &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
+                {% endblock %}
+            </div>
+        </body>
+    </html>
+
+In this example, the :doc:`block<tags/block>` tags define four blocks that
+child templates can fill in. All the ``block`` tag does is to tell the
+template engine that a child template may override those portions of the
+template.
+
+A child template might look like this:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block title %}Index{% endblock %}
+    {% block head %}
+        {{ parent() }}
+        <style type="text/css">
+            .important { color: #336699; }
+        </style>
+    {% endblock %}
+    {% block content %}
+        <h1>Index</h1>
+        <p class="important">
+            Welcome on my awesome homepage.
+        </p>
+    {% endblock %}
+
+The :doc:`extends<tags/extends>` tag is the key here. It tells the template
+engine that this template "extends" another template. When the template system
+evaluates this template, first it locates the parent. The extends tag should
+be the first tag in the template.
+
+Note that since the child template doesn't define the ``footer`` block, the
+value from the parent template is used instead.
+
+It's possible to render the contents of the parent block by using the
+:doc:`parent<functions/parent>` function. This gives back the results of the
+parent block:
+
+.. code-block:: jinja
+
+    {% block sidebar %}
+        <h3>Table Of Contents</h3>
+        ...
+        {{ parent() }}
+    {% endblock %}
+
+.. tip::
+
+    The documentation page for the :doc:`extends<tags/extends>` tag describes
+    more advanced features like block nesting, scope, dynamic inheritance, and
+    conditional inheritance.
+
+.. note::
+
+    Twig also supports multiple inheritance with the so called horizontal reuse
+    with the help of the :doc:`use<tags/use>` tag. This is an advanced feature
+    hardly ever needed in regular templates.
+
+HTML Escaping
+-------------
+
+When generating HTML from templates, there's always a risk that a variable
+will include characters that affect the resulting HTML. There are two
+approaches: manually escaping each variable or automatically escaping
+everything by default.
+
+Twig supports both, automatic escaping is enabled by default.
+
+.. note::
+
+    Automatic escaping is only supported if the *escaper* extension has been
+    enabled (which is the default).
+
+Working with Manual Escaping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If manual escaping is enabled it's **your** responsibility to escape variables
+if needed. What to escape? If you have a variable that *may* include any of
+the following chars (``>``, ``<``, ``&``, or ``"``) you **have to** escape it
+unless the variable contains well-formed and trusted HTML. Escaping works by
+piping the variable through the :doc:`escape<filters/escape>` or ``e`` filter:
+
+.. code-block:: jinja
+
+    {{ user.username|e }}
+    {{ user.username|e('js') }}
+
+Working with Automatic Escaping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Whether automatic escaping is enabled or not, you can mark a section of a
+template to be escaped or not by using the :doc:`autoescape<tags/autoescape>`
+tag:
+
+.. code-block:: jinja
+
+    {% autoescape true %}
+        Everything will be automatically escaped in this block
+    {% endautoescape %}
+
+Escaping
+--------
+
+It is sometimes desirable or even necessary to have Twig ignore parts it would
+otherwise handle as variables or blocks. For example if the default syntax is
+used and you want to use ``{{`` as raw string in the template and not start a
+variable you have to use a trick.
+
+The easiest way is to output the variable delimiter (``{{``) by using a variable
+expression:
+
+.. code-block:: jinja
+
+    {{ '{{' }}
+
+For bigger sections it makes sense to mark a block :doc:`raw<tags/raw>`.
+
+Macros
+------
+
+Macros are comparable with functions in regular programming languages. They
+are useful to put often used HTML idioms into reusable elements to not repeat
+yourself.
+
+A macro is defined via the :doc:`macro<tags/macro>` tag. Here is a small
+example of a macro that renders a form element:
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+Macros can be defined in any template, and need to be "imported" before being
+used via the :doc:`import<tags/import>` tag:
+
+.. code-block:: jinja
+
+    {% import "forms.html" as forms %}
+
+    <p>{{ forms.input('username') }}</p>
+
+Alternatively you can import names from the template into the current
+namespace via the :doc:`from<tags/from>` tag:
+
+.. code-block:: jinja
+
+    {% from 'forms.html' import input as input_field, textarea %}
+
+    <dl>
+        <dt>Username</dt>
+        <dd>{{ input_field('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ input_field('password', type='password') }}</dd>
+    </dl>
+    <p>{{ textarea('comment') }}</p>
+
+Expressions
+-----------
+
+Twig allows expressions everywhere. These work very similar to regular PHP and
+even if you're not working with PHP you should feel comfortable with it.
+
+.. note::
+
+    The operator precedence is as follows, with the lowest-precedence
+    operators listed first: ``&``, ``^``, ``|``, ``or``, ``and``, ``==``,
+    ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``..``, ``+``, ``-``, ``~``,
+    ``*``, ``/``, ``//``, ``%``, ``is``, and ``**``.
+
+Literals
+~~~~~~~~
+
+.. versionadded:: 1.5
+    Support for hash keys as names and expressions was added in Twig 1.5.
+
+The simplest form of expressions are literals. Literals are representations
+for PHP types such as strings, numbers, and arrays. The following literals
+exist:
+
+* ``"Hello World"``: Everything between two double or single quotes is a
+  string. They are useful whenever you need a string in the template (for
+  example as arguments to function calls, filters or just to extend or
+  include a template).
+
+* ``42`` / ``42.23``: Integers and floating point numbers are created by just
+  writing the number down. If a dot is present the number is a float,
+  otherwise an integer.
+
+* ``["foo", "bar"]``: Arrays are defined by a sequence of expressions
+  separated by a comma (``,``) and wrapped with squared brackets (``[]``).
+
+* ``{"foo": "bar"}``: Hashes are defined by a list of keys and values
+  separated by a comma (``,``) and wrapped with curly braces (``{}``):
+
+  .. code-block:: jinja
+
+    {# keys as string #}
+    { 'foo': 'foo', 'bar': 'bar' }
+
+    {# keys as names (equivalent to the previous hash) -- as of Twig 1.5 #}
+    { foo: 'foo', bar: 'bar' }
+
+    {# keys as integer #}
+    { 2: 'foo', 4: 'bar' }
+
+    {# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #}
+    { (1 + 1): 'foo', (a ~ 'b'): 'bar' }
+
+* ``true`` / ``false``: ``true`` represents the true value, ``false``
+  represents the false value.
+
+* ``null``: ``null`` represents no specific value. This is the value returned
+  when a variable does not exist. ``none`` is an alias for ``null``.
+
+Arrays and hashes can be nested:
+
+.. code-block:: jinja
+
+    {% set foo = [1, {"foo": "bar"}] %}
+
+Math
+~~~~
+
+Twig allows you to calculate with values. This is rarely useful in templates
+but exists for completeness' sake. The following operators are supported:
+
+* ``+``: Adds two objects together (the operands are casted to numbers). ``{{
+  1 + 1 }}`` is ``2``.
+
+* ``-``: Substracts the second number from the first one. ``{{ 3 - 2 }}`` is
+  ``1``.
+
+* ``/``: Divides two numbers. The return value will be a floating point
+  number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``.
+
+* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is
+  ``4``.
+
+* ``//``: Divides two numbers and returns the truncated integer result. ``{{
+  20 // 7 }}`` is ``2``.
+
+* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would
+  return ``4``.
+
+* ``**``: Raises the left operand to the power of the right operand. ``{{ 2 **
+  3 }}`` would return ``8``.
+
+Logic
+~~~~~
+
+You can combine multiple expressions with the following operators:
+
+* ``and``: Returns true if the left and the right operands are both true.
+
+* ``or``: Returns true if the left or the right operand is true.
+
+* ``not``: Negates a statement.
+
+* ``(expr)``: Groups an expression.
+
+Comparisons
+~~~~~~~~~~~
+
+The following comparison operators are supported in any expression: ``==``,
+``!=``, ``<``, ``>``, ``>=``, and ``<=``.
+
+Containment Operator
+~~~~~~~~~~~~~~~~~~~~
+
+The ``in`` operator performs containment test.
+
+It returns ``true`` if the left operand is contained in the right:
+
+.. code-block:: jinja
+
+    {# returns true #}
+
+    {{ 1 in [1, 2, 3] }}
+
+    {{ 'cd' in 'abcde' }}
+
+.. tip::
+
+    You can use this filter to perform a containment test on strings, arrays,
+    or objects implementing the ``Traversable`` interface.
+
+To perform a negative test, use the ``not in`` operator:
+
+.. code-block:: jinja
+
+    {% if 1 not in [1, 2, 3] %}
+
+    {# is equivalent to #}
+    {% if not (1 in [1, 2, 3]) %}
+
+Test Operator
+~~~~~~~~~~~~~
+
+The ``is`` operator performs tests. Tests can be used to test a variable against
+a common expression. The right operand is name of the test:
+
+.. code-block:: jinja
+
+    {# find out if a variable is odd #}
+
+    {{ name is odd }}
+
+Tests can accept arguments too:
+
+.. code-block:: jinja
+
+    {% if loop.index is divisibleby(3) %}
+
+Tests can be negated by using the ``is not`` operator:
+
+.. code-block:: jinja
+
+    {% if loop.index is not divisibleby(3) %}
+
+    {# is equivalent to #}
+    {% if not (loop.index is divisibleby(3)) %}
+
+Go to the :doc:`tests<tests/index>` page to learn more about the built-in
+tests.
+
+Other Operators
+~~~~~~~~~~~~~~~
+
+The following operators are very useful but don't fit into any of the other
+categories:
+
+* ``..``: Creates a sequence based on the operand before and after the
+  operator (this is just syntactic sugar for the :doc:`range<functions/range>`
+  function).
+
+* ``|``: Applies a filter.
+
+* ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello
+  " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello
+  John!``.
+
+* ``.``, ``[]``: Gets an attribute of an object.
+
+* ``?:``: The PHP ternary operator: ``{{ foo ? 'yes' : 'no' }}``
+
+String Interpolation
+~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.5
+    String interpolation was added in Twig 1.5.
+
+String interpolation (`#{expression}`) allows any valid expression to appear
+within a string. The result of evaluating that expression is inserted into the
+string:
+
+.. code-block:: jinja
+
+    {{ "foo #{bar} baz" }}
+    {{ "foo #{1 + 2} baz" }}
+
+Whitespace Control
+------------------
+
+.. versionadded:: 1.1
+    Tag level whitespace control was added in Twig 1.1.
+
+The first newline after a template tag is removed automatically (like in PHP.)
+Whitespace is not further modified by the template engine, so each whitespace
+(spaces, tabs, newlines etc.) is returned unchanged.
+
+Use the ``spaceless`` tag to remove whitespace *between HTML tags*:
+
+.. code-block:: jinja
+
+    {% spaceless %}
+        <div>
+            <strong>foo</strong>
+        </div>
+    {% endspaceless %}
+
+    {# output will be <div><strong>foo</strong></div> #}
+
+In addition to the spaceless tag you can also control whitespace on a per tag
+level. By using the whitespace control modifier on your tags, you can trim
+leading and or trailing whitespace:
+
+.. code-block:: jinja
+
+    {% set value = 'no spaces' %}
+    {#- No leading/trailing whitespace -#}
+    {%- if true -%}
+        {{- value -}}
+    {%- endif -%}
+
+    {# output 'no spaces' #}
+
+The above sample shows the default whitespace control modifier, and how you can
+use it to remove whitespace around tags.  Trimming space will consume all whitespace
+for that side of the tag.  It is possible to use whitespace trimming on one side
+of a tag:
+
+.. code-block:: jinja
+
+    {% set value = 'no spaces' %}
+    <li>    {{- value }}    </li>
+
+    {# outputs '<li>no spaces    </li>' #}
+
+Extensions
+----------
+
+Twig can be easily extended.
+
+If you are looking for new tags, filters, or functions, have a look at the Twig official
+`extension repository`_.
+
+If you want to create your own, read :doc:`extensions`.
+
+.. _`Twig bundle`:              https://github.com/Anomareh/PHP-Twig.tmbundle
+.. _`Jinja syntax plugin`:      http://jinja.pocoo.org/2/documentation/integration
+.. _`Twig syntax plugin`:       http://plugins.netbeans.org/plugin/37069/php-twig
+.. _`Twig plugin`:              https://github.com/pulse00/Twig-Eclipse-Plugin
+.. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language
+.. _`extension repository`:     http://github.com/fabpot/Twig-extensions
+.. _`Twig syntax mode`:         https://github.com/bobthecow/Twig-HTML.mode

+ 11 - 0
lib/twig/doc/tests/constant.rst

@@ -0,0 +1,11 @@
+``constant``
+============
+
+``constant`` checks if a variable has the exact same value as a constant. You
+can use either global constants or class constants:
+
+.. code-block:: jinja
+
+    {% if post.status is constant('Post::PUBLISHED') %}
+        the status attribute is exactly the same as Post::PUBLISHED
+    {% endif %}

+ 30 - 0
lib/twig/doc/tests/defined.rst

@@ -0,0 +1,30 @@
+``defined``
+===========
+
+``defined`` checks if a variable is defined in the current context. This is very
+useful if you use the ``strict_variables`` option:
+
+.. code-block:: jinja
+
+    {# defined works with variable names #}
+    {% if foo is defined %}
+        ...
+    {% endif %}
+
+    {# and attributes on variables names #}
+    {% if foo.bar is defined %}
+        ...
+    {% endif %}
+
+    {% if foo['bar'] is defined %}
+        ...
+    {% endif %}
+
+When using the ``defined`` test on an expression that uses variables in some
+method calls, be sure that they are all defined first:
+
+.. code-block:: jinja
+
+    {% if var is defined and foo.method(var) is defined %}
+        ...
+    {% endif %}

+ 10 - 0
lib/twig/doc/tests/divisibleby.rst

@@ -0,0 +1,10 @@
+``divisibleby``
+===============
+
+``divisibleby`` checks if a variable is divisible by a number:
+
+.. code-block:: jinja
+
+    {% if loop.index is divisibleby(3) %}
+        ...
+    {% endif %}

+ 11 - 0
lib/twig/doc/tests/empty.rst

@@ -0,0 +1,11 @@
+``empty``
+=========
+
+``empty`` checks if a variable is empty:
+
+.. code-block:: jinja
+
+    {# evaluates to true if the foo variable is null, false, or the empty string #}
+    {% if foo is empty %}
+        ...
+    {% endif %}

+ 10 - 0
lib/twig/doc/tests/even.rst

@@ -0,0 +1,10 @@
+``even``
+========
+
+``even`` returns ``true`` if the given number is even:
+
+.. code-block:: jinja
+
+    {{ var is even }}
+
+.. seealso:: :doc:`odd<../tests/odd>`

+ 14 - 0
lib/twig/doc/tests/index.rst

@@ -0,0 +1,14 @@
+Tests
+=====
+
+.. toctree::
+    :maxdepth: 1
+
+    divisibleby
+    null
+    even
+    odd
+    sameas
+    constant
+    defined
+    empty

+ 12 - 0
lib/twig/doc/tests/null.rst

@@ -0,0 +1,12 @@
+``null``
+========
+
+``null`` returns ``true`` if the variable is ``null``:
+
+.. code-block:: jinja
+
+    {{ var is null }}
+
+.. note::
+
+    ``none`` is an alias for ``null``.

+ 10 - 0
lib/twig/doc/tests/odd.rst

@@ -0,0 +1,10 @@
+``odd``
+=======
+
+``odd`` returns ``true`` if the given number is odd:
+
+.. code-block:: jinja
+
+    {{ var is odd }}
+
+.. seealso:: :doc:`even<../tests/even>`

+ 11 - 0
lib/twig/doc/tests/sameas.rst

@@ -0,0 +1,11 @@
+``sameas``
+==========
+
+``sameas`` checks if a variable points to the same memory address than another
+variable:
+
+.. code-block:: jinja
+
+    {% if foo.attribute is sameas(false) %}
+        the foo attribute really is the ``false`` PHP value
+    {% endif %}

+ 30 - 0
lib/twig/ext/twig/.gitignore

@@ -0,0 +1,30 @@
+*.sw*
+.deps
+Makefile
+Makefile.fragments
+Makefile.global
+Makefile.objects
+acinclude.m4
+aclocal.m4
+build/
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.nice
+config.status
+config.sub
+configure
+configure.in
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+run-tests.php
+twig.loT
+.libs/
+modules/
+twig.la
+twig.lo

+ 22 - 0
lib/twig/ext/twig/LICENSE

@@ -0,0 +1,22 @@
+Copyright (c) 2011, Derick Rethans <derick@derickrethans.nl>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright 
+      notice, this list of conditions and the following disclaimer in the 
+      documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 8 - 0
lib/twig/ext/twig/config.m4

@@ -0,0 +1,8 @@
+dnl config.m4 for extension twig
+
+PHP_ARG_ENABLE(twig, whether to enable twig support,
+[  --enable-twig           Enable twig support])
+
+if test "$PHP_TWIG" != "no"; then
+  PHP_NEW_EXTENSION(twig, twig.c, $ext_shared)
+fi

+ 8 - 0
lib/twig/ext/twig/config.w32

@@ -0,0 +1,8 @@
+// vim:ft=javascript
+
+ARG_ENABLE("twig", "Twig support", "no");
+
+if (PHP_TWIG != "no") {
+	AC_DEFINE('HAVE_TWIG', 1);
+	EXTENSION('twig', 'twig.c');
+}

+ 49 - 0
lib/twig/ext/twig/php_twig.h

@@ -0,0 +1,49 @@
+/*
+   +----------------------------------------------------------------------+
+   | Twig Extension                                                       |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2011 Derick Rethans                                    |
+   +----------------------------------------------------------------------+
+   | Redistribution and use in source and binary forms, with or without   |
+   | modification, are permitted provided that the conditions mentioned   |
+   | in the accompanying LICENSE file are met (BSD, revised).             |
+   +----------------------------------------------------------------------+
+   | Author: Derick Rethans <derick@derickrethans.nl>                     |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_TWIG_H
+#define PHP_TWIG_H
+
+#define PHP_TWIG_VERSION "1.6.4"
+
+#include "php.h"
+
+extern zend_module_entry twig_module_entry;
+#define phpext_twig_ptr &twig_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_TWIG_API __declspec(dllexport)
+#else
+#define PHP_TWIG_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_FUNCTION(twig_template_get_attributes);
+
+PHP_MINIT_FUNCTION(twig);
+PHP_MSHUTDOWN_FUNCTION(twig);
+PHP_RINIT_FUNCTION(twig);
+PHP_RSHUTDOWN_FUNCTION(twig);
+PHP_MINFO_FUNCTION(twig);
+
+#ifdef ZTS
+#define TWIG_G(v) TSRMG(twig_globals_id, zend_twig_globals *, v)
+#else
+#define TWIG_G(v) (twig_globals.v)
+#endif
+
+#endif

+ 1025 - 0
lib/twig/ext/twig/twig.c

@@ -0,0 +1,1025 @@
+/*
+   +----------------------------------------------------------------------+
+   | Twig Extension                                                       |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2011 Derick Rethans                                    |
+   +----------------------------------------------------------------------+
+   | Redistribution and use in source and binary forms, with or without   |
+   | modification, are permitted provided that the conditions mentioned   |
+   | in the accompanying LICENSE file are met (BSD, revised).             |
+   +----------------------------------------------------------------------+
+   | Author: Derick Rethans <derick@derickrethans.nl>                     |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_twig.h"
+#include "ext/standard/php_string.h"
+#include "ext/standard/php_smart_str.h"
+
+#include "Zend/zend_object_handlers.h"
+#include "Zend/zend_interfaces.h"
+#include "Zend/zend_exceptions.h"
+
+#ifndef Z_ADDREF_P
+#define Z_ADDREF_P(pz)                (pz)->refcount++
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 6)
+	ZEND_ARG_INFO(0, template)
+	ZEND_ARG_INFO(0, object)
+	ZEND_ARG_INFO(0, item)
+	ZEND_ARG_INFO(0, arguments)
+	ZEND_ARG_INFO(0, type)
+	ZEND_ARG_INFO(0, isDefinedTest)
+ZEND_END_ARG_INFO()
+
+zend_function_entry twig_functions[] = {
+	PHP_FE(twig_template_get_attributes, twig_template_get_attribute_args)
+	{NULL, NULL, NULL}
+};
+
+
+zend_module_entry twig_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+	STANDARD_MODULE_HEADER,
+#endif
+	"twig",
+	twig_functions,
+	PHP_MINIT(twig),
+	PHP_MSHUTDOWN(twig),
+	PHP_RINIT(twig),
+	PHP_RSHUTDOWN(twig),
+	PHP_MINFO(twig),
+#if ZEND_MODULE_API_NO >= 20010901
+	PHP_TWIG_VERSION,
+#endif
+	STANDARD_MODULE_PROPERTIES
+};
+
+
+#ifdef COMPILE_DL_TWIG
+ZEND_GET_MODULE(twig)
+#endif
+
+PHP_INI_BEGIN()
+PHP_INI_END()
+
+PHP_MINIT_FUNCTION(twig)
+{
+	REGISTER_INI_ENTRIES();
+
+	return SUCCESS;
+}
+
+
+PHP_MSHUTDOWN_FUNCTION(twig)
+{
+	UNREGISTER_INI_ENTRIES();
+
+	return SUCCESS;
+}
+
+
+
+PHP_RINIT_FUNCTION(twig)
+{
+	return SUCCESS;
+}
+
+
+
+PHP_RSHUTDOWN_FUNCTION(twig)
+{
+	return SUCCESS;
+}
+
+
+PHP_MINFO_FUNCTION(twig)
+{
+	php_info_print_table_start();
+	php_info_print_table_header(2, "Twig support", "enabled");
+	php_info_print_table_row(2, "Version", PHP_TWIG_VERSION);
+	php_info_print_table_end();
+
+	DISPLAY_INI_ENTRIES();
+
+}
+
+int TWIG_ARRAY_KEY_EXISTS(zval *array, char* key, int key_len)
+{
+	if (Z_TYPE_P(array) != IS_ARRAY) {
+		return 0;
+	}
+	return zend_symtable_exists(Z_ARRVAL_P(array), key, key_len + 1);
+}
+
+int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
+{
+	if (Z_TYPE_P(object) != IS_OBJECT) {
+		return 0;
+	}
+	return instanceof_function(Z_OBJCE_P(object), interface TSRMLS_CC);
+}
+
+int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
+{
+	zend_class_entry **pce;
+	if (Z_TYPE_P(object) != IS_OBJECT) {
+		return 0;
+	}
+	if (zend_lookup_class(interface, strlen(interface), &pce TSRMLS_CC) == FAILURE) {
+		return 0;
+	}
+	return instanceof_function(Z_OBJCE_P(object), *pce TSRMLS_CC);
+}
+
+zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
+{
+    zend_class_entry *ce = Z_OBJCE_P(object);
+    zval *retval;
+
+	if (Z_TYPE_P(object) == IS_OBJECT) {
+		SEPARATE_ARG_IF_REF(offset);
+		zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
+
+        zval_ptr_dtor(&offset);
+
+        if (!retval) {
+            if (!EG(exception)) {
+                zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
+            }
+            return NULL;
+        }
+
+        return retval;
+	}
+	return NULL;
+}
+
+int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
+{
+	zend_class_entry *ce = Z_OBJCE_P(object);
+	zval *retval;
+
+	if (Z_TYPE_P(object) == IS_OBJECT) {
+		SEPARATE_ARG_IF_REF(offset);
+		zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
+
+		zval_ptr_dtor(&offset);
+
+		if (!retval) {
+			if (!EG(exception)) {
+				zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
+			}
+			return 0;
+		}
+
+		return (retval && Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval));
+	}
+	return 0;
+}
+
+char *TWIG_STRTOLOWER(const char *str, int str_len)
+{
+	char *item_dup;
+
+	item_dup = estrndup(str, str_len);
+	php_strtolower(item_dup, str_len);
+	return item_dup;
+}
+
+zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TSRMLS_DC)
+{
+	zend_fcall_info fci;
+	zval ***args = NULL;
+	int arg_count = 0;
+	HashTable *table;
+	HashPosition pos;
+	int i = 0;
+	zval *retval_ptr;
+	zval *zfunction;
+
+	if (arguments) {
+		table = HASH_OF(arguments);
+		args = safe_emalloc(sizeof(zval **), table->nNumOfElements, 0);
+
+		zend_hash_internal_pointer_reset_ex(table, &pos);
+
+		while (zend_hash_get_current_data_ex(table, (void **)&args[i], &pos) == SUCCESS) {
+			i++;
+			zend_hash_move_forward_ex(table, &pos);
+		}
+		arg_count = table->nNumOfElements;
+	}
+
+	MAKE_STD_ZVAL(zfunction);
+	ZVAL_STRING(zfunction, function, 1);
+	fci.size = sizeof(fci);
+	fci.function_table = EG(function_table);
+	fci.function_name = zfunction;
+	fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+	fci.object_ptr = object;
+#else
+	fci.object_pp = &object;
+#endif
+	fci.retval_ptr_ptr = &retval_ptr;
+	fci.param_count = arg_count;
+	fci.params = args;
+	fci.no_separation = 0;
+
+	if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+		zval_dtor(zfunction);
+		efree(zfunction);
+		zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Could not execute %s::%s()", zend_get_class_entry(object TSRMLS_CC)->name, function TSRMLS_CC);
+	}
+
+	if (args) {
+		efree(fci.params);
+	}
+	zval_dtor(zfunction);
+	efree(zfunction);
+	return retval_ptr;
+}
+
+int TWIG_CALL_BOOLEAN(zval *object, char *functionName TSRMLS_DC)
+{
+	zval *ret;
+	int   res;
+
+	ret = TWIG_CALL_USER_FUNC_ARRAY(object, functionName, NULL TSRMLS_CC);
+	res = Z_LVAL_P(ret);
+	zval_ptr_dtor(&ret);
+	return res;
+}
+
+zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
+{
+	zval **tmp_zval;
+	zend_class_entry *ce;
+
+	if (class == NULL || Z_TYPE_P(class) != IS_OBJECT) {
+		return NULL;
+	}
+
+	ce = zend_get_class_entry(class TSRMLS_CC);
+#if PHP_VERSION_ID >= 50400
+	tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0, NULL TSRMLS_CC);
+#else
+	tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0 TSRMLS_CC);
+#endif
+	return *tmp_zval;
+}
+
+zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
+{
+	zval **tmp_zval;
+	char *tmp_name;
+
+	if (class == NULL || Z_TYPE_P(class) != IS_ARRAY || Z_TYPE_P(prop_name) != IS_STRING) {
+		if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
+			// array access object
+			return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
+		}
+		return NULL;
+	}
+
+	convert_to_string(prop_name);
+	tmp_name = Z_STRVAL_P(prop_name);
+	if (zend_symtable_find(HASH_OF(class), tmp_name, strlen(tmp_name)+1, (void**) &tmp_zval) == SUCCESS) {
+		return *tmp_zval;
+	}
+	return NULL;
+}
+
+zval *TWIG_GET_ARRAY_ELEMENT(zval *class, char *prop_name, int prop_name_length TSRMLS_DC)
+{
+	zval **tmp_zval;
+
+	if (class == NULL/* || Z_TYPE_P(class) != IS_ARRAY*/) {
+		return NULL;
+	}
+
+	if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
+		// array access object
+		zval *tmp_name_zval;
+		zval *tmp_ret_zval;
+
+		ALLOC_INIT_ZVAL(tmp_name_zval);
+		ZVAL_STRING(tmp_name_zval, prop_name, 1);
+		tmp_ret_zval = TWIG_GET_ARRAYOBJECT_ELEMENT(class, tmp_name_zval TSRMLS_CC);
+		zval_dtor(tmp_name_zval);
+		efree(tmp_name_zval);
+		return tmp_ret_zval;
+	}
+
+	if (zend_symtable_find(HASH_OF(class), prop_name, prop_name_length+1, (void**)&tmp_zval) == SUCCESS) {
+		return *tmp_zval;
+	}
+	return NULL;
+}
+
+zval *TWIG_PROPERTY(zval *object, zval *propname TSRMLS_DC)
+{
+	char *prot_name;
+	int prot_name_length;
+	zval *tmp = NULL;
+
+	tmp = TWIG_GET_ARRAY_ELEMENT(object, Z_STRVAL_P(propname), Z_STRLEN_P(propname) TSRMLS_CC);
+	if (tmp) {
+		return tmp;
+	}
+
+	zend_mangle_property_name(&prot_name, &prot_name_length, "*", 1, Z_STRVAL_P(propname), Z_STRLEN_P(propname), 0);
+	tmp = TWIG_GET_ARRAY_ELEMENT(object, prot_name, prot_name_length TSRMLS_CC);
+	efree(prot_name);
+	if (tmp) {
+		return tmp;
+	}
+
+	if (Z_OBJ_HT_P(object)->read_property) {
+#if PHP_VERSION_ID >= 50400
+		tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS, NULL TSRMLS_CC);
+#else
+		tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS TSRMLS_CC);
+#endif
+		if (tmp != EG(uninitialized_zval_ptr)) {
+			return tmp;
+		} else {
+			return NULL;
+		}
+	}
+	return tmp;
+}
+
+int TWIG_HAS_PROPERTY(zval *object, zval *propname TSRMLS_DC)
+{
+	if (Z_OBJ_HT_P(object)->has_property) {
+#if PHP_VERSION_ID >= 50400
+		return Z_OBJ_HT_P(object)->has_property(object, propname, 0, NULL TSRMLS_CC);
+#else
+		return Z_OBJ_HT_P(object)->has_property(object, propname, 0 TSRMLS_CC);
+#endif
+	}
+	return 0;
+}
+
+zval *TWIG_PROPERTY_CHAR(zval *object, char *propname TSRMLS_DC)
+{
+	zval *tmp_name_zval, *tmp;
+
+	ALLOC_INIT_ZVAL(tmp_name_zval);
+	ZVAL_STRING(tmp_name_zval, propname, 1);
+	tmp = TWIG_PROPERTY(object, tmp_name_zval TSRMLS_CC);
+	zval_dtor(tmp_name_zval);
+	efree(tmp_name_zval);
+	return tmp;
+}
+
+int TWIG_CALL_B_0(zval *object, char *method)
+{
+	return 0;
+}
+
+zval *TWIG_CALL_S(zval *object, char *method, char *arg0 TSRMLS_DC)
+{
+	zend_fcall_info fci;
+	zval **args[1];
+	zval *argument;
+	zval *zfunction;
+	zval *retval_ptr;
+
+	MAKE_STD_ZVAL(argument);
+	ZVAL_STRING(argument, arg0, 1);
+	args[0] = &argument;
+
+	MAKE_STD_ZVAL(zfunction);
+	ZVAL_STRING(zfunction, method, 1);
+	fci.size = sizeof(fci);
+	fci.function_table = EG(function_table);
+	fci.function_name = zfunction;
+	fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+	fci.object_ptr = object;
+#else
+	fci.object_pp = &object;
+#endif
+	fci.retval_ptr_ptr = &retval_ptr;
+	fci.param_count = 1;
+	fci.params = args;
+	fci.no_separation = 0;
+
+	if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+		zval_dtor(argument);
+		return 0;
+	}
+	zval_dtor(zfunction);
+	efree(zfunction);
+	zval_dtor(argument);
+	efree(argument);
+	return retval_ptr;
+}
+
+int TWIG_CALL_SB(zval *object, char *method, char *arg0 TSRMLS_DC)
+{
+	zval *retval_ptr;
+	int success;
+
+	retval_ptr = TWIG_CALL_S(object, method, arg0 TSRMLS_CC);
+	success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
+
+	if (retval_ptr) {
+		zval_ptr_dtor(&retval_ptr);
+	}
+
+	return success;
+}
+
+int TWIG_CALL_Z(zval *object, char *method, zval *arg1 TSRMLS_DC)
+{
+	zend_fcall_info fci;
+	zval **args[1];
+	zval *zfunction;
+	zval *retval_ptr;
+	int   success;
+
+	args[0] = &arg1;
+
+	MAKE_STD_ZVAL(zfunction);
+	ZVAL_STRING(zfunction, method, 1);
+	fci.size = sizeof(fci);
+	fci.function_table = EG(function_table);
+	fci.function_name = zfunction;
+	fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+	fci.object_ptr = object;
+#else
+	fci.object_pp = &object;
+#endif
+	fci.retval_ptr_ptr = &retval_ptr;
+	fci.param_count = 1;
+	fci.params = args;
+	fci.no_separation = 0;
+
+	if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+		zval_dtor(zfunction);
+		efree(zfunction);
+		if (retval_ptr) {
+			zval_ptr_dtor(&retval_ptr);
+		}
+		return 0;
+	}
+
+	zval_dtor(zfunction);
+	efree(zfunction);
+
+	success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
+	if (retval_ptr) {
+		zval_ptr_dtor(&retval_ptr);
+	}
+
+	return success;
+}
+
+int TWIG_CALL_ZZ(zval *object, char *method, zval *arg1, zval *arg2 TSRMLS_DC)
+{
+	zend_fcall_info fci;
+	zval **args[2];
+	zval *zfunction;
+	zval *retval_ptr;
+	int   success;
+
+	args[0] = &arg1;
+	args[1] = &arg2;
+
+	MAKE_STD_ZVAL(zfunction);
+	ZVAL_STRING(zfunction, method, 1);
+	fci.size = sizeof(fci);
+	fci.function_table = EG(function_table);
+	fci.function_name = zfunction;
+	fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+	fci.object_ptr = object;
+#else
+	fci.object_pp = &object;
+#endif
+	fci.retval_ptr_ptr = &retval_ptr;
+	fci.param_count = 2;
+	fci.params = args;
+	fci.no_separation = 0;
+
+	if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+		zval_dtor(zfunction);
+		return 0;
+	}
+
+	zval_dtor(zfunction);
+
+	success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
+	if (retval_ptr) {
+		zval_ptr_dtor(&retval_ptr);
+	}
+
+	return success;
+}
+
+#ifndef Z_SET_REFCOUNT_P
+# define Z_SET_REFCOUNT_P(pz, rc)  pz->refcount = rc
+# define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
+#endif
+
+void TWIG_NEW(zval *object, char *class, zval *arg0, zval *arg1 TSRMLS_DC)
+{
+	zend_class_entry **pce;
+
+	if (zend_lookup_class(class, strlen(class), &pce TSRMLS_CC) == FAILURE) {
+		return;
+	}
+
+	Z_TYPE_P(object) = IS_OBJECT;
+	object_init_ex(object, *pce);
+	Z_SET_REFCOUNT_P(object, 1);
+	Z_UNSET_ISREF_P(object);
+
+	TWIG_CALL_ZZ(object, "__construct", arg0, arg1 TSRMLS_CC);
+}
+
+static int twig_add_array_key_to_string(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+	smart_str *buf;
+	char *joiner;
+
+	buf = va_arg(args, smart_str*);
+	joiner = va_arg(args, char*);
+
+	if (buf->len != 0) {
+		smart_str_appends(buf, joiner);
+	}
+
+	if (hash_key->nKeyLength == 0) {
+		smart_str_append_long(buf, (long) hash_key->h);
+	} else {
+		char *key, *tmp_str;
+		int key_len, tmp_len;
+		key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
+		tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
+
+		smart_str_appendl(buf, tmp_str, tmp_len);
+		efree(key);
+		efree(tmp_str);
+	}
+
+	return 0;
+}
+
+char *TWIG_IMPLODE_ARRAY_KEYS(char *joiner, zval *array TSRMLS_DC)
+{
+	smart_str collector = { 0, 0, 0 };
+
+	smart_str_appendl(&collector, "", 0);
+	zend_hash_apply_with_arguments(HASH_OF(array) TSRMLS_CC, twig_add_array_key_to_string, 2, &collector, joiner);
+	smart_str_0(&collector);
+
+	return collector.c;
+}
+
+static void TWIG_THROW_EXCEPTION(char *exception_name TSRMLS_DC, char *message, ...)
+{
+	char *buffer;
+	va_list args;
+	zend_class_entry **pce;
+
+	if (zend_lookup_class(exception_name, strlen(exception_name), &pce TSRMLS_CC) == FAILURE)
+	{
+		return;
+	}
+
+	va_start(args, message);
+	vspprintf(&buffer, 0, message, args);
+	va_end(args);
+
+	zend_throw_exception_ex(*pce, 0 TSRMLS_CC, buffer);
+}
+
+char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
+{
+	char *class_name;
+	zend_uint class_name_len;
+
+	if (Z_TYPE_P(object) != IS_OBJECT) {
+		return "";
+	}
+	zend_get_object_classname(object, &class_name, &class_name_len TSRMLS_CC);
+	return class_name;
+}
+
+static int twig_add_method_to_class(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+	zval *retval;
+	char *item;
+	size_t item_len;
+	zend_function *mptr = (zend_function *) pDest;
+
+	if (!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
+		return 0;
+	}
+
+	retval = va_arg(args, zval*);
+
+	item_len = strlen(mptr->common.function_name);
+	item = estrndup(mptr->common.function_name, item_len);
+	php_strtolower(item, item_len);
+
+	add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
+
+	return 0;
+}
+
+static int twig_add_property_to_class(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+	zend_class_entry *ce;
+	zval *retval;
+	char *class_name, *prop_name;
+	zend_property_info *pptr = (zend_property_info *) pDest;
+
+	if (!(pptr->flags & ZEND_ACC_PUBLIC)) {
+		return 0;
+	}
+
+	ce = *va_arg(args, zend_class_entry**);
+	retval = va_arg(args, zval*);
+
+	zend_unmangle_property_name(pptr->name, pptr->name_length, &class_name, &prop_name);
+
+	add_assoc_string(retval, prop_name, prop_name, 1);
+
+	return 0;
+}
+
+/* {{{ _adddynproperty */
+static int twig_add_dyn_property_to_class(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+	zend_class_entry *ce = *va_arg(args, zend_class_entry**);
+	zval *retval = va_arg(args, zval*), member;
+	char *class_name, *prop_name;
+
+	if (hash_key->nKeyLength < 1 || hash_key->arKey[0] == '\0') {
+		return 0; /* non public cannot be dynamic */
+	}
+
+	ZVAL_STRINGL(&member, hash_key->arKey, hash_key->nKeyLength-1, 0);
+	if (zend_get_property_info(ce, &member, 1 TSRMLS_CC) == &EG(std_property_info)) {
+		zend_unmangle_property_name((&EG(std_property_info))->name, (&EG(std_property_info))->name_length, &class_name, &prop_name);
+		add_assoc_string(retval, prop_name, prop_name, 1);
+	}
+	return 0;
+}
+
+static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name TSRMLS_DC)
+{
+	zval *class_info, *class_methods, *class_properties;
+	zend_class_entry *class_ce;
+
+	class_ce = zend_get_class_entry(object TSRMLS_CC);
+
+	ALLOC_INIT_ZVAL(class_info);
+	ALLOC_INIT_ZVAL(class_methods);
+	ALLOC_INIT_ZVAL(class_properties);
+	array_init(class_info);
+	array_init(class_methods);
+	array_init(class_properties);
+	// add all methods to self::cache[$class]['methods']
+	zend_hash_apply_with_arguments(&class_ce->function_table TSRMLS_CC, twig_add_method_to_class, 1, class_methods);
+	zend_hash_apply_with_arguments(&class_ce->properties_info TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
+
+	if (object && Z_OBJ_HT_P(object)->get_properties) {
+		HashTable *properties = Z_OBJ_HT_P(object)->get_properties(object TSRMLS_CC);
+		zend_hash_apply_with_arguments(properties TSRMLS_CC, twig_add_dyn_property_to_class, 2, &class_ce, class_properties);
+	}
+	add_assoc_zval(class_info, "methods", class_methods);
+	add_assoc_zval(class_info, "properties", class_properties);
+	add_assoc_zval(cache, class_name, class_info);
+}
+
+/* {{{ proto mixed twig_template_get_attributes(TwigTemplate template, mixed object, mixed item, array arguments, string type, boolean isDefinedTest, boolean ignoreStrictCheck)
+   A C implementation of TwigTemplate::getAttribute() */
+PHP_FUNCTION(twig_template_get_attributes)
+{
+	zval *template;
+	zval *object;
+	char *item;
+	int  item_len;
+	zval  zitem;
+	zval *arguments = NULL;
+	zval *ret = NULL;
+	char *type = NULL;
+	int   type_len = 0;
+	zend_bool isDefinedTest = 0;
+	zend_bool ignoreStrictCheck = 0;
+	int free_ret = 0;
+	zval *tmp_self_cache;
+
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozs|asbb", &template, &object, &item, &item_len, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
+		return;
+	}
+
+	INIT_PZVAL(&zitem);
+	ZVAL_STRINGL(&zitem, item, item_len, 0);
+
+	if (!type) {
+		type = "any";
+	}
+
+/*
+	// array
+	if (Twig_TemplateInterface::METHOD_CALL !== $type) {
+		if ((is_array($object) && array_key_exists($item, $object))
+			|| ($object instanceof ArrayAccess && isset($object[$item]))
+		) {
+			if ($isDefinedTest) {
+				return true;
+			}
+
+			return $object[$item];
+		}
+*/
+	if (strcmp("method", type) != 0) {
+//		printf("XXXmethod: %s\n", type);
+		if ((TWIG_ARRAY_KEY_EXISTS(object, item, item_len))
+			|| (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, &zitem TSRMLS_CC))
+		) {
+			zval *ret;
+
+			if (isDefinedTest) {
+				RETURN_TRUE;
+			}
+
+			ret = TWIG_GET_ARRAY_ELEMENT(object, item, item_len TSRMLS_CC);
+			RETVAL_ZVAL(ret, 1, 0);
+			if (free_ret) {
+				zval_ptr_dtor(&ret);
+			}
+			return;
+		}
+/*
+		if (Twig_TemplateInterface::ARRAY_CALL === $type) {
+			if ($isDefinedTest) {
+				return false;
+			}
+			if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
+				return null;
+			}
+*/
+		if (strcmp("array", type) == 0) {
+			if (isDefinedTest) {
+				RETURN_FALSE;
+			}
+			if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
+				return;
+			}
+/*
+			if (is_object($object)) {
+				throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)));
+			// array
+			} else {
+				throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))));
+			}
+		}
+	}
+*/
+			if (Z_TYPE_P(object) == IS_OBJECT) {
+				TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Key \"%s\" in object (with ArrayAccess) of type \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
+			} else {
+				TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC));
+			}
+			return;
+		}
+	}
+
+/*
+	if (!is_object($object)) {
+		if ($isDefinedTest) {
+			return false;
+		}
+*/
+
+	if (Z_TYPE_P(object) != IS_OBJECT) {
+		if (isDefinedTest) {
+			RETURN_FALSE;
+		}
+/*
+		if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
+			return null;
+		}
+		throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, implode(', ', array_keys($object))));
+	}
+*/
+		if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
+			RETURN_FALSE;
+		}
+		if (Z_TYPE_P(object) == IS_ARRAY) {
+			TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Item \"%s\" for \"Array\" does not exist", item);
+		} else {
+			Z_ADDREF_P(object);
+			convert_to_string_ex(&object);
+			TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Item \"%s\" for \"%s\" does not exist", item, Z_STRVAL_P(object));
+			zval_ptr_dtor(&object);
+		}
+		return;
+	}
+/*
+	// get some information about the object
+	$class = get_class($object);
+	if (!isset(self::$cache[$class])) {
+		$r = new ReflectionClass($class);
+		self::$cache[$class] = array('methods' => array(), 'properties' => array());
+		foreach ($r->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
+			self::$cache[$class]['methods'][strtolower($method->getName())] = true;
+		}
+
+		foreach ($r->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
+			self::$cache[$class]['properties'][$property->getName()] = true;
+		}
+	}
+*/
+	if (Z_TYPE_P(object) == IS_OBJECT) {
+		char *class_name = NULL;
+
+		class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
+		tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
+
+		if (!TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC)) {
+			twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
+		}
+		efree(class_name);
+	}
+
+/*
+	// object property
+	if (Twig_TemplateInterface::METHOD_CALL !== $type) {
+		if (isset(self::$cache[$class]['properties'][$item])
+			|| isset($object->$item) || array_key_exists($item, $object)
+		) {
+			if ($isDefinedTest) {
+				return true;
+			}
+			if ($this->env->hasExtension('sandbox')) {
+				$this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
+			}
+
+			return $object->$item;
+		}
+	}
+*/
+	if (strcmp("method", type) != 0) {
+		zval *tmp_class, *tmp_properties, *tmp_item;
+		char *class_name = NULL;
+
+		class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
+		tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
+		tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
+		tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
+
+		efree(class_name);
+
+		if (tmp_item || TWIG_HAS_PROPERTY(object, &zitem TSRMLS_CC) || TWIG_ARRAY_KEY_EXISTS(object, item, item_len) // FIXME: Array key? is that array access here?
+		) {
+			if (isDefinedTest) {
+				RETURN_TRUE;
+			}
+			if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
+				TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, &zitem TSRMLS_CC);
+			}
+			if (EG(exception)) {
+				return;
+			}
+
+			ret = TWIG_PROPERTY(object, &zitem TSRMLS_CC);
+			RETURN_ZVAL(ret, 1, 0);
+		}
+	}
+/*
+	// object method
+	$lcItem = strtolower($item);
+	if (isset(self::$cache[$class]['methods'][$lcItem])) {
+		$method = $item;
+	} elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
+		$method = 'get'.$item;
+	} elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
+		$method = 'is'.$item;
+	} elseif (isset(self::$cache[$class]['methods']['__call'])) {
+		$method = $item;
+*/
+	{
+		char *lcItem = TWIG_STRTOLOWER(item, item_len);
+		int   lcItem_length;
+		char *method = NULL;
+		char *tmp_method_name_get;
+		char *tmp_method_name_is;
+		zval *tmp_class, *tmp_methods;
+		char *class_name = NULL;
+
+		class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
+		lcItem_length = strlen(lcItem);
+		tmp_method_name_get = emalloc(4 + lcItem_length);
+		tmp_method_name_is  = emalloc(3 + lcItem_length);
+
+		sprintf(tmp_method_name_get, "get%s", lcItem);
+		sprintf(tmp_method_name_is, "is%s", lcItem);
+
+		tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
+		tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
+		efree(class_name);
+
+		if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
+			method = item;
+		} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_get, lcItem_length + 3 TSRMLS_CC)) {
+			method = tmp_method_name_get;
+		} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_is, lcItem_length + 2 TSRMLS_CC)) {
+			method = tmp_method_name_is;
+		} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
+			method = item;
+/*
+	} else {
+		if ($isDefinedTest) {
+			return false;
+		}
+		if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
+			return null;
+		}
+		throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)));
+	}
+	if ($isDefinedTest) {
+		return true;
+	}
+*/
+		} else {
+			if (isDefinedTest) {
+				RETURN_FALSE;
+			}
+			if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
+				return;
+			}
+			TWIG_THROW_EXCEPTION("Twig_Error_Runtime" TSRMLS_CC, "Method \"%s\" for object \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
+			return;
+		}
+		if (isDefinedTest) {
+			efree(tmp_method_name_get);
+			efree(tmp_method_name_is);
+			efree(lcItem);
+			RETURN_TRUE;
+		}
+/*
+	if ($this->env->hasExtension('sandbox')) {
+		$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
+	}
+*/
+		if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
+			TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, &zitem TSRMLS_CC);
+		}
+		if (EG(exception)) {
+			return;
+		}
+/*
+	$ret = call_user_func_array(array($object, $method), $arguments);
+*/
+		if (Z_TYPE_P(object) == IS_OBJECT) {
+			ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
+			free_ret = 1;
+		}
+		efree(tmp_method_name_get);
+		efree(tmp_method_name_is);
+		efree(lcItem);
+	}
+/*
+	if ($object instanceof Twig_TemplateInterface) {
+		return new Twig_Markup($ret, $this->env->getCharset());
+	}
+*/
+	if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
+		zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
+		TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
+		zval_ptr_dtor(&charset);
+		if (ret) {
+			zval_ptr_dtor(&ret);
+		}
+		return;
+	}
+/*
+	return $ret;
+*/
+	if (ret) {
+		RETVAL_ZVAL(ret, 1, 0);
+		if (free_ret) {
+			zval_ptr_dtor(&ret);
+		}
+	}
+}

+ 46 - 0
lib/twig/lib/Twig/Autoloader.php

@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Autoloads Twig classes.
+ *
+ * @package twig
+ * @author  Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Autoloader
+{
+    /**
+     * Registers Twig_Autoloader as an SPL autoloader.
+     */
+    static public function register()
+    {
+        ini_set('unserialize_callback_func', 'spl_autoload_call');
+        spl_autoload_register(array(new self, 'autoload'));
+    }
+
+    /**
+     * Handles autoloading of classes.
+     *
+     * @param  string  $class  A class name.
+     *
+     * @return boolean Returns true if the class has been loaded
+     */
+    static public function autoload($class)
+    {
+        if (0 !== strpos($class, 'Twig')) {
+            return;
+        }
+
+        if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) {
+            require $file;
+        }
+    }
+}

+ 242 - 0
lib/twig/lib/Twig/Compiler.php

@@ -0,0 +1,242 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ * (c) 2009 Armin Ronacher
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Compiles a node to PHP code.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Compiler implements Twig_CompilerInterface
+{
+    protected $lastLine;
+    protected $source;
+    protected $indentation;
+    protected $env;
+    protected $debugInfo;
+    protected $sourceOffset;
+    protected $sourceLine;
+
+    /**
+     * Constructor.
+     *
+     * @param Twig_Environment $env The twig environment instance
+     */
+    public function __construct(Twig_Environment $env)
+    {
+        $this->env = $env;
+        $this->debugInfo = array();
+    }
+
+    /**
+     * Returns the environment instance related to this compiler.
+     *
+     * @return Twig_Environment The environment instance
+     */
+    public function getEnvironment()
+    {
+        return $this->env;
+    }
+
+    /**
+     * Gets the current PHP code after compilation.
+     *
+     * @return string The PHP code
+     */
+    public function getSource()
+    {
+        return $this->source;
+    }
+
+    /**
+     * Compiles a node.
+     *
+     * @param Twig_NodeInterface $node        The node to compile
+     * @param integer            $indentation The current indentation
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function compile(Twig_NodeInterface $node, $indentation = 0)
+    {
+        $this->lastLine = null;
+        $this->source = '';
+        $this->sourceOffset = 0;
+        $this->sourceLine = 0;
+        $this->indentation = $indentation;
+
+        $node->compile($this);
+
+        return $this;
+    }
+
+    public function subcompile(Twig_NodeInterface $node, $raw = true)
+    {
+        if (false === $raw) {
+            $this->addIndentation();
+        }
+
+        $node->compile($this);
+
+        return $this;
+    }
+
+    /**
+     * Adds a raw string to the compiled code.
+     *
+     * @param  string $string The string
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function raw($string)
+    {
+        $this->source .= $string;
+
+        return $this;
+    }
+
+    /**
+     * Writes a string to the compiled code by adding indentation.
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function write()
+    {
+        $strings = func_get_args();
+        foreach ($strings as $string) {
+            $this->addIndentation();
+            $this->source .= $string;
+        }
+
+        return $this;
+    }
+
+    public function addIndentation()
+    {
+        $this->source .= str_repeat(' ', $this->indentation * 4);
+
+        return $this;
+    }
+
+    /**
+     * Adds a quoted string to the compiled code.
+     *
+     * @param  string $value The string
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function string($value)
+    {
+        $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
+
+        return $this;
+    }
+
+    /**
+     * Returns a PHP representation of a given value.
+     *
+     * @param  mixed $value The value to convert
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function repr($value)
+    {
+        if (is_int($value) || is_float($value)) {
+            if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
+                setlocale(LC_NUMERIC, 'C');
+            }
+
+            $this->raw($value);
+
+            if (false !== $locale) {
+                setlocale(LC_NUMERIC, $locale);
+            }
+        } elseif (null === $value) {
+            $this->raw('null');
+        } elseif (is_bool($value)) {
+            $this->raw($value ? 'true' : 'false');
+        } elseif (is_array($value)) {
+            $this->raw('array(');
+            $i = 0;
+            foreach ($value as $key => $value) {
+                if ($i++) {
+                    $this->raw(', ');
+                }
+                $this->repr($key);
+                $this->raw(' => ');
+                $this->repr($value);
+            }
+            $this->raw(')');
+        } else {
+            $this->string($value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Adds debugging information.
+     *
+     * @param Twig_NodeInterface $node The related twig node
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function addDebugInfo(Twig_NodeInterface $node)
+    {
+        if ($node->getLine() != $this->lastLine) {
+            $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
+            $this->sourceOffset = strlen($this->source);
+            $this->debugInfo[$this->sourceLine] = $node->getLine();
+
+            $this->lastLine = $node->getLine();
+            $this->write("// line {$node->getLine()}\n");
+        }
+
+        return $this;
+    }
+
+    public function getDebugInfo()
+    {
+        return $this->debugInfo;
+    }
+
+    /**
+     * Indents the generated code.
+     *
+     * @param integer $step The number of indentation to add
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function indent($step = 1)
+    {
+        $this->indentation += $step;
+
+        return $this;
+    }
+
+    /**
+     * Outdents the generated code.
+     *
+     * @param integer $step The number of indentation to remove
+     *
+     * @return Twig_Compiler The current compiler instance
+     */
+    public function outdent($step = 1)
+    {
+        $this->indentation -= $step;
+
+        if ($this->indentation < 0) {
+            throw new Twig_Error('Unable to call outdent() as the indentation would become negative');
+        }
+
+        return $this;
+    }
+}

+ 35 - 0
lib/twig/lib/Twig/CompilerInterface.php

@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface implemented by compiler classes.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien@symfony.com>
+ */
+interface Twig_CompilerInterface
+{
+    /**
+     * Compiles a node.
+     *
+     * @param  Twig_NodeInterface $node The node to compile
+     *
+     * @return Twig_CompilerInterface The current compiler instance
+     */
+    function compile(Twig_NodeInterface $node);
+
+    /**
+     * Gets the current PHP code after compilation.
+     *
+     * @return string The PHP code
+     */
+    function getSource();
+}

+ 1060 - 0
lib/twig/lib/Twig/Environment.php

@@ -0,0 +1,1060 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stores the Twig configuration.
+ *
+ * @package twig
+ * @author  Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Environment
+{
+    const VERSION = '1.6.4';
+
+    protected $charset;
+    protected $loader;
+    protected $debug;
+    protected $autoReload;
+    protected $cache;
+    protected $lexer;
+    protected $parser;
+    protected $compiler;
+    protected $baseTemplateClass;
+    protected $extensions;
+    protected $parsers;
+    protected $visitors;
+    protected $filters;
+    protected $tests;
+    protected $functions;
+    protected $globals;
+    protected $runtimeInitialized;
+    protected $loadedTemplates;
+    protected $strictVariables;
+    protected $unaryOperators;
+    protected $binaryOperators;
+    protected $templateClassPrefix = '__TwigTemplate_';
+    protected $functionCallbacks;
+    protected $filterCallbacks;
+    protected $staging;
+
+    /**
+     * Constructor.
+     *
+     * Available options:
+     *
+     *  * debug: When set to `true`, the generated templates have a __toString()
+     *           method that you can use to display the generated nodes (default to
+     *           false).
+     *
+     *  * charset: The charset used by the templates (default to utf-8).
+     *
+     *  * base_template_class: The base template class to use for generated
+     *                         templates (default to Twig_Template).
+     *
+     *  * cache: An absolute path where to store the compiled templates, or
+     *           false to disable compilation cache (default)
+     *
+     *  * auto_reload: Whether to reload the template is the original source changed.
+     *                 If you don't provide the auto_reload option, it will be
+     *                 determined automatically base on the debug value.
+     *
+     *  * strict_variables: Whether to ignore invalid variables in templates
+     *                      (default to false).
+     *
+     *  * autoescape: Whether to enable auto-escaping (default to true);
+     *
+     *  * optimizations: A flag that indicates which optimizations to apply
+     *                   (default to -1 which means that all optimizations are enabled;
+     *                   set it to 0 to disable)
+     *
+     * @param Twig_LoaderInterface   $loader  A Twig_LoaderInterface instance
+     * @param array                  $options An array of options
+     */
+    public function __construct(Twig_LoaderInterface $loader = null, $options = array())
+    {
+        if (null !== $loader) {
+            $this->setLoader($loader);
+        }
+
+        $options = array_merge(array(
+            'debug'               => false,
+            'charset'             => 'UTF-8',
+            'base_template_class' => 'Twig_Template',
+            'strict_variables'    => false,
+            'autoescape'          => true,
+            'cache'               => false,
+            'auto_reload'         => null,
+            'optimizations'       => -1,
+        ), $options);
+
+        $this->debug              = (bool) $options['debug'];
+        $this->charset            = $options['charset'];
+        $this->baseTemplateClass  = $options['base_template_class'];
+        $this->autoReload         = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
+        $this->extensions         = array(
+            'core'      => new Twig_Extension_Core(),
+            'escaper'   => new Twig_Extension_Escaper((bool) $options['autoescape']),
+            'optimizer' => new Twig_Extension_Optimizer($options['optimizations']),
+        );
+        $this->strictVariables    = (bool) $options['strict_variables'];
+        $this->runtimeInitialized = false;
+        $this->setCache($options['cache']);
+        $this->functionCallbacks = array();
+        $this->filterCallbacks = array();
+    }
+
+    /**
+     * Gets the base template class for compiled templates.
+     *
+     * @return string The base template class name
+     */
+    public function getBaseTemplateClass()
+    {
+        return $this->baseTemplateClass;
+    }
+
+    /**
+     * Sets the base template class for compiled templates.
+     *
+     * @param string $class The base template class name
+     */
+    public function setBaseTemplateClass($class)
+    {
+        $this->baseTemplateClass = $class;
+    }
+
+    /**
+     * Enables debugging mode.
+     */
+    public function enableDebug()
+    {
+        $this->debug = true;
+    }
+
+    /**
+     * Disables debugging mode.
+     */
+    public function disableDebug()
+    {
+        $this->debug = false;
+    }
+
+    /**
+     * Checks if debug mode is enabled.
+     *
+     * @return Boolean true if debug mode is enabled, false otherwise
+     */
+    public function isDebug()
+    {
+        return $this->debug;
+    }
+
+    /**
+     * Enables the auto_reload option.
+     */
+    public function enableAutoReload()
+    {
+        $this->autoReload = true;
+    }
+
+    /**
+     * Disables the auto_reload option.
+     */
+    public function disableAutoReload()
+    {
+        $this->autoReload = false;
+    }
+
+    /**
+     * Checks if the auto_reload option is enabled.
+     *
+     * @return Boolean true if auto_reload is enabled, false otherwise
+     */
+    public function isAutoReload()
+    {
+        return $this->autoReload;
+    }
+
+    /**
+     * Enables the strict_variables option.
+     */
+    public function enableStrictVariables()
+    {
+        $this->strictVariables = true;
+    }
+
+    /**
+     * Disables the strict_variables option.
+     */
+    public function disableStrictVariables()
+    {
+        $this->strictVariables = false;
+    }
+
+    /**
+     * Checks if the strict_variables option is enabled.
+     *
+     * @return Boolean true if strict_variables is enabled, false otherwise
+     */
+    public function isStrictVariables()
+    {
+        return $this->strictVariables;
+    }
+
+    /**
+     * Gets the cache directory or false if cache is disabled.
+     *
+     * @return string|false
+     */
+    public function getCache()
+    {
+        return $this->cache;
+    }
+
+     /**
+      * Sets the cache directory or false if cache is disabled.
+      *
+      * @param string|false $cache The absolute path to the compiled templates,
+      *                            or false to disable cache
+      */
+    public function setCache($cache)
+    {
+        $this->cache = $cache ? $cache : false;
+    }
+
+    /**
+     * Gets the cache filename for a given template.
+     *
+     * @param string $name The template name
+     *
+     * @return string The cache file name
+     */
+    public function getCacheFilename($name)
+    {
+        if (false === $this->cache) {
+            return false;
+        }
+
+        $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix));
+
+        return $this->getCache().'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php';
+    }
+
+    /**
+     * Gets the template class associated with the given string.
+     *
+     * @param string $name The name for which to calculate the template class name
+     *
+     * @return string The template class name
+     */
+    public function getTemplateClass($name)
+    {
+        return $this->templateClassPrefix.md5($this->loader->getCacheKey($name));
+    }
+
+    /**
+     * Gets the template class prefix.
+     *
+     * @return string The template class prefix
+     */
+    public function getTemplateClassPrefix()
+    {
+        return $this->templateClassPrefix;
+    }
+
+    /**
+     * Renders a template.
+     *
+     * @param string $name    The template name
+     * @param array  $context An array of parameters to pass to the template
+     *
+     * @return string The rendered template
+     */
+    public function render($name, array $context = array())
+    {
+        return $this->loadTemplate($name)->render($context);
+    }
+
+    /**
+     * Displays a template.
+     *
+     * @param string $name    The template name
+     * @param array  $context An array of parameters to pass to the template
+     */
+    public function display($name, array $context = array())
+    {
+        $this->loadTemplate($name)->display($context);
+    }
+
+    /**
+     * Loads a template by name.
+     *
+     * @param  string  $name  The template name
+     *
+     * @return Twig_TemplateInterface A template instance representing the given template name
+     */
+    public function loadTemplate($name)
+    {
+        $cls = $this->getTemplateClass($name);
+
+        if (isset($this->loadedTemplates[$cls])) {
+            return $this->loadedTemplates[$cls];
+        }
+
+        if (!class_exists($cls, false)) {
+            if (false === $cache = $this->getCacheFilename($name)) {
+                eval('?>'.$this->compileSource($this->loader->getSource($name), $name));
+            } else {
+                if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
+                    $this->writeCacheFile($cache, $this->compileSource($this->loader->getSource($name), $name));
+                }
+
+                require_once $cache;
+            }
+        }
+
+        if (!$this->runtimeInitialized) {
+            $this->initRuntime();
+        }
+
+        return $this->loadedTemplates[$cls] = new $cls($this);
+    }
+
+    /**
+     * Returns true if the template is still fresh.
+     *
+     * Besides checking the loader for freshness information,
+     * this method also checks if the enabled extensions have
+     * not changed.
+     *
+     * @param string    $name The template name
+     * @param timestamp $time The last modification time of the cached template
+     *
+     * @return Boolean true if the template is fresh, false otherwise
+     */
+    public function isTemplateFresh($name, $time)
+    {
+        foreach ($this->extensions as $extension) {
+            $r = new ReflectionObject($extension);
+            if (filemtime($r->getFileName()) > $time) {
+                return false;
+            }
+        }
+
+        return $this->loader->isFresh($name, $time);
+    }
+
+    public function resolveTemplate($names)
+    {
+        if (!is_array($names)) {
+            $names = array($names);
+        }
+
+        foreach ($names as $name) {
+            if ($name instanceof Twig_Template) {
+                return $name;
+            }
+
+            try {
+                return $this->loadTemplate($name);
+            } catch (Twig_Error_Loader $e) {
+            }
+        }
+
+        if (1 === count($names)) {
+            throw $e;
+        }
+
+        throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
+    }
+
+    /**
+     * Clears the internal template cache.
+     */
+    public function clearTemplateCache()
+    {
+        $this->loadedTemplates = array();
+    }
+
+    /**
+     * Clears the template cache files on the filesystem.
+     */
+    public function clearCacheFiles()
+    {
+        if (false === $this->cache) {
+            return;
+        }
+
+        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
+            if ($file->isFile()) {
+                @unlink($file->getPathname());
+            }
+        }
+    }
+
+    /**
+     * Gets the Lexer instance.
+     *
+     * @return Twig_LexerInterface A Twig_LexerInterface instance
+     */
+    public function getLexer()
+    {
+        if (null === $this->lexer) {
+            $this->lexer = new Twig_Lexer($this);
+        }
+
+        return $this->lexer;
+    }
+
+    /**
+     * Sets the Lexer instance.
+     *
+     * @param Twig_LexerInterface A Twig_LexerInterface instance
+     */
+    public function setLexer(Twig_LexerInterface $lexer)
+    {
+        $this->lexer = $lexer;
+    }
+
+    /**
+     * Tokenizes a source code.
+     *
+     * @param string $source The template source code
+     * @param string $name   The template name
+     *
+     * @return Twig_TokenStream A Twig_TokenStream instance
+     */
+    public function tokenize($source, $name = null)
+    {
+        return $this->getLexer()->tokenize($source, $name);
+    }
+
+    /**
+     * Gets the Parser instance.
+     *
+     * @return Twig_ParserInterface A Twig_ParserInterface instance
+     */
+    public function getParser()
+    {
+        if (null === $this->parser) {
+            $this->parser = new Twig_Parser($this);
+        }
+
+        return $this->parser;
+    }
+
+    /**
+     * Sets the Parser instance.
+     *
+     * @param Twig_ParserInterface A Twig_ParserInterface instance
+     */
+    public function setParser(Twig_ParserInterface $parser)
+    {
+        $this->parser = $parser;
+    }
+
+    /**
+     * Parses a token stream.
+     *
+     * @param Twig_TokenStream $tokens A Twig_TokenStream instance
+     *
+     * @return Twig_Node_Module A Node tree
+     */
+    public function parse(Twig_TokenStream $tokens)
+    {
+        return $this->getParser()->parse($tokens);
+    }
+
+    /**
+     * Gets the Compiler instance.
+     *
+     * @return Twig_CompilerInterface A Twig_CompilerInterface instance
+     */
+    public function getCompiler()
+    {
+        if (null === $this->compiler) {
+            $this->compiler = new Twig_Compiler($this);
+        }
+
+        return $this->compiler;
+    }
+
+    /**
+     * Sets the Compiler instance.
+     *
+     * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance
+     */
+    public function setCompiler(Twig_CompilerInterface $compiler)
+    {
+        $this->compiler = $compiler;
+    }
+
+    /**
+     * Compiles a Node.
+     *
+     * @param Twig_NodeInterface $node A Twig_NodeInterface instance
+     *
+     * @return string The compiled PHP source code
+     */
+    public function compile(Twig_NodeInterface $node)
+    {
+        return $this->getCompiler()->compile($node)->getSource();
+    }
+
+    /**
+     * Compiles a template source code.
+     *
+     * @param string $source The template source code
+     * @param string $name   The template name
+     *
+     * @return string The compiled PHP source code
+     */
+    public function compileSource($source, $name = null)
+    {
+        try {
+            return $this->compile($this->parse($this->tokenize($source, $name)));
+        } catch (Twig_Error $e) {
+            $e->setTemplateFile($name);
+            throw $e;
+        } catch (Exception $e) {
+            throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
+        }
+    }
+
+    /**
+     * Sets the Loader instance.
+     *
+     * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
+     */
+    public function setLoader(Twig_LoaderInterface $loader)
+    {
+        $this->loader = $loader;
+    }
+
+    /**
+     * Gets the Loader instance.
+     *
+     * @return Twig_LoaderInterface A Twig_LoaderInterface instance
+     */
+    public function getLoader()
+    {
+        return $this->loader;
+    }
+
+    /**
+     * Sets the default template charset.
+     *
+     * @param string $charset The default charset
+     */
+    public function setCharset($charset)
+    {
+        $this->charset = $charset;
+    }
+
+    /**
+     * Gets the default template charset.
+     *
+     * @return string The default charset
+     */
+    public function getCharset()
+    {
+        return $this->charset;
+    }
+
+    /**
+     * Initializes the runtime environment.
+     */
+    public function initRuntime()
+    {
+        $this->runtimeInitialized = true;
+
+        foreach ($this->getExtensions() as $extension) {
+            $extension->initRuntime($this);
+        }
+    }
+
+    /**
+     * Returns true if the given extension is registered.
+     *
+     * @param string $name The extension name
+     *
+     * @return Boolean Whether the extension is registered or not
+     */
+    public function hasExtension($name)
+    {
+        return isset($this->extensions[$name]);
+    }
+
+    /**
+     * Gets an extension by name.
+     *
+     * @param string $name The extension name
+     *
+     * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance
+     */
+    public function getExtension($name)
+    {
+        if (!isset($this->extensions[$name])) {
+            throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name));
+        }
+
+        return $this->extensions[$name];
+    }
+
+    /**
+     * Registers an extension.
+     *
+     * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance
+     */
+    public function addExtension(Twig_ExtensionInterface $extension)
+    {
+        $this->extensions[$extension->getName()] = $extension;
+        $this->parsers = null;
+        $this->visitors = null;
+        $this->filters = null;
+        $this->tests = null;
+        $this->functions = null;
+        $this->globals = null;
+    }
+
+    /**
+     * Removes an extension by name.
+     *
+     * @param string $name The extension name
+     */
+    public function removeExtension($name)
+    {
+        unset($this->extensions[$name]);
+        $this->parsers = null;
+        $this->visitors = null;
+        $this->filters = null;
+        $this->tests = null;
+        $this->functions = null;
+        $this->globals = null;
+    }
+
+    /**
+     * Registers an array of extensions.
+     *
+     * @param array $extensions An array of extensions
+     */
+    public function setExtensions(array $extensions)
+    {
+        foreach ($extensions as $extension) {
+            $this->addExtension($extension);
+        }
+    }
+
+    /**
+     * Returns all registered extensions.
+     *
+     * @return array An array of extensions
+     */
+    public function getExtensions()
+    {
+        return $this->extensions;
+    }
+
+    /**
+     * Registers a Token Parser.
+     *
+     * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
+     */
+    public function addTokenParser(Twig_TokenParserInterface $parser)
+    {
+        $this->staging['token_parsers'][] = $parser;
+        $this->parsers = null;
+    }
+
+    /**
+     * Gets the registered Token Parsers.
+     *
+     * @return Twig_TokenParserBrokerInterface A broker containing token parsers
+     */
+    public function getTokenParsers()
+    {
+        if (null === $this->parsers) {
+            $this->parsers = new Twig_TokenParserBroker();
+
+            if (isset($this->staging['token_parsers'])) {
+                foreach ($this->staging['token_parsers'] as $parser) {
+                    $this->parsers->addTokenParser($parser);
+                }
+            }
+
+            foreach ($this->getExtensions() as $extension) {
+                $parsers = $extension->getTokenParsers();
+                foreach($parsers as $parser) {
+                    if ($parser instanceof Twig_TokenParserInterface) {
+                        $this->parsers->addTokenParser($parser);
+                    } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
+                        $this->parsers->addTokenParserBroker($parser);
+                    } else {
+                        throw new Twig_Error_Runtime('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
+                    }
+                }
+            }
+        }
+
+        return $this->parsers;
+    }
+
+    /**
+     * Gets registered tags.
+     *
+     * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
+     *
+     * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
+     */
+    public function getTags()
+    {
+        $tags = array();
+        foreach ($this->getTokenParsers()->getParsers() as $parser) {
+            if ($parser instanceof Twig_TokenParserInterface) {
+                $tags[$parser->getTag()] = $parser;
+            }
+        }
+
+        return $tags;
+    }
+
+    /**
+     * Registers a Node Visitor.
+     *
+     * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
+     */
+    public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
+    {
+        $this->staging['visitors'][] = $visitor;
+        $this->visitors = null;
+    }
+
+    /**
+     * Gets the registered Node Visitors.
+     *
+     * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
+     */
+    public function getNodeVisitors()
+    {
+        if (null === $this->visitors) {
+            $this->visitors = isset($this->staging['visitors']) ? $this->staging['visitors'] : array();
+            foreach ($this->getExtensions() as $extension) {
+                $this->visitors = array_merge($this->visitors, $extension->getNodeVisitors());
+            }
+        }
+
+        return $this->visitors;
+    }
+
+    /**
+     * Registers a Filter.
+     *
+     * @param string               $name   The filter name
+     * @param Twig_FilterInterface $filter A Twig_FilterInterface instance
+     */
+    public function addFilter($name, Twig_FilterInterface $filter)
+    {
+        $this->staging['filters'][$name] = $filter;
+        $this->filters = null;
+    }
+
+    /**
+     * Get a filter by name.
+     *
+     * Subclasses may override this method and load filters differently;
+     * so no list of filters is available.
+     *
+     * @param string $name The filter name
+     *
+     * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exists
+     */
+    public function getFilter($name)
+    {
+        if (null === $this->filters) {
+            $this->getFilters();
+        }
+
+        if (isset($this->filters[$name])) {
+            return $this->filters[$name];
+        }
+
+        foreach ($this->filters as $pattern => $filter) {
+            $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
+
+            if ($count) {
+                if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
+                    array_shift($matches);
+                    $filter->setArguments($matches);
+
+                    return $filter;
+                }
+            }
+        }
+
+        foreach ($this->filterCallbacks as $callback) {
+            if (false !== $filter = call_user_func($callback, $name)) {
+                return $filter;
+            }
+        }
+
+        return false;
+    }
+
+    public function registerUndefinedFilterCallback($callable)
+    {
+        $this->filterCallbacks[] = $callable;
+    }
+
+    /**
+     * Gets the registered Filters.
+     *
+     * Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback.
+     *
+     * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
+     *
+     * @see registerUndefinedFilterCallback
+     */
+    public function getFilters()
+    {
+        if (null === $this->filters) {
+            $this->filters = isset($this->staging['filters']) ? $this->staging['filters'] : array();
+            foreach ($this->getExtensions() as $extension) {
+                $this->filters = array_merge($this->filters, $extension->getFilters());
+            }
+        }
+
+        return $this->filters;
+    }
+
+    /**
+     * Registers a Test.
+     *
+     * @param string             $name The test name
+     * @param Twig_TestInterface $test A Twig_TestInterface instance
+     */
+    public function addTest($name, Twig_TestInterface $test)
+    {
+        $this->staging['tests'][$name] = $test;
+        $this->tests = null;
+    }
+
+    /**
+     * Gets the registered Tests.
+     *
+     * @return Twig_TestInterface[] An array of Twig_TestInterface instances
+     */
+    public function getTests()
+    {
+        if (null === $this->tests) {
+            $this->tests = isset($this->staging['tests']) ? $this->staging['tests'] : array();
+            foreach ($this->getExtensions() as $extension) {
+                $this->tests = array_merge($this->tests, $extension->getTests());
+            }
+        }
+
+        return $this->tests;
+    }
+
+    /**
+     * Registers a Function.
+     *
+     * @param string                 $name     The function name
+     * @param Twig_FunctionInterface $function A Twig_FunctionInterface instance
+     */
+    public function addFunction($name, Twig_FunctionInterface $function)
+    {
+        $this->staging['functions'][$name] = $function;
+        $this->functions = null;
+    }
+
+    /**
+     * Get a function by name.
+     *
+     * Subclasses may override this method and load functions differently;
+     * so no list of functions is available.
+     *
+     * @param string $name function name
+     *
+     * @return Twig_Function|false A Twig_Function instance or false if the function does not exists
+     */
+    public function getFunction($name)
+    {
+        if (null === $this->functions) {
+            $this->getFunctions();
+        }
+
+        if (isset($this->functions[$name])) {
+            return $this->functions[$name];
+        }
+
+        foreach ($this->functions as $pattern => $function) {
+            $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
+
+            if ($count) {
+                if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
+                    array_shift($matches);
+                    $function->setArguments($matches);
+
+                    return $function;
+                }
+            }
+        }
+
+        foreach ($this->functionCallbacks as $callback) {
+            if (false !== $function = call_user_func($callback, $name)) {
+                return $function;
+            }
+        }
+
+        return false;
+    }
+
+    public function registerUndefinedFunctionCallback($callable)
+    {
+        $this->functionCallbacks[] = $callable;
+    }
+
+    /**
+     * Gets registered functions.
+     *
+     * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
+     *
+     * @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances
+     *
+     * @see registerUndefinedFunctionCallback
+     */
+    public function getFunctions()
+    {
+        if (null === $this->functions) {
+            $this->functions = isset($this->staging['functions']) ? $this->staging['functions'] : array();
+            foreach ($this->getExtensions() as $extension) {
+                $this->functions = array_merge($this->functions, $extension->getFunctions());
+            }
+        }
+
+        return $this->functions;
+    }
+
+    /**
+     * Registers a Global.
+     *
+     * @param string $name  The global name
+     * @param mixed  $value The global value
+     */
+    public function addGlobal($name, $value)
+    {
+        $this->staging['globals'][$name] = $value;
+        $this->globals = null;
+    }
+
+    /**
+     * Gets the registered Globals.
+     *
+     * @return array An array of globals
+     */
+    public function getGlobals()
+    {
+        if (null === $this->globals) {
+            $this->globals = isset($this->staging['globals']) ? $this->staging['globals'] : array();
+            foreach ($this->getExtensions() as $extension) {
+                $this->globals = array_merge($this->globals, $extension->getGlobals());
+            }
+        }
+
+        return $this->globals;
+    }
+
+    /**
+     * Gets the registered unary Operators.
+     *
+     * @return array An array of unary operators
+     */
+    public function getUnaryOperators()
+    {
+        if (null === $this->unaryOperators) {
+            $this->initOperators();
+        }
+
+        return $this->unaryOperators;
+    }
+
+    /**
+     * Gets the registered binary Operators.
+     *
+     * @return array An array of binary operators
+     */
+    public function getBinaryOperators()
+    {
+        if (null === $this->binaryOperators) {
+            $this->initOperators();
+        }
+
+        return $this->binaryOperators;
+    }
+
+    public function computeAlternatives($name, $items)
+    {
+        $alternatives = array();
+        foreach ($items as $item) {
+            $lev = levenshtein($name, $item);
+            if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
+                $alternatives[$item] = $lev;
+            }
+        }
+        asort($alternatives);
+
+        return array_keys($alternatives);
+    }
+
+    protected function initOperators()
+    {
+        $this->unaryOperators = array();
+        $this->binaryOperators = array();
+        foreach ($this->getExtensions() as $extension) {
+            $operators = $extension->getOperators();
+
+            if (!$operators) {
+                continue;
+            }
+
+            if (2 !== count($operators)) {
+                throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
+            }
+
+            $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
+            $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
+        }
+    }
+
+    protected function writeCacheFile($file, $content)
+    {
+        $dir = dirname($file);
+        if (!is_dir($dir)) {
+            if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
+                throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir));
+            }
+        } elseif (!is_writable($dir)) {
+            throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir));
+        }
+
+        $tmpFile = tempnam(dirname($file), basename($file));
+        if (false !== @file_put_contents($tmpFile, $content)) {
+            // rename does not work on Win32 before 5.2.6
+            if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) {
+                chmod($file, 0644);
+
+                return;
+            }
+        }
+
+        throw new Twig_Error_Runtime(sprintf('Failed to write cache file "%s".', $file));
+    }
+}

+ 176 - 0
lib/twig/lib/Twig/Error.php

@@ -0,0 +1,176 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Twig base exception.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Error extends Exception
+{
+    protected $lineno;
+    protected $filename;
+    protected $rawMessage;
+    protected $previous;
+
+    /**
+     * Constructor.
+     *
+     * @param string    $message  The error message
+     * @param integer   $lineno   The template line where the error occurred
+     * @param string    $filename The template file name where the error occurred
+     * @param Exception $previous The previous exception
+     */
+    public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
+    {
+        if (-1 === $lineno || null === $filename) {
+            if ($trace = $this->getTemplateTrace()) {
+                if (-1 === $lineno) {
+                    $lineno = $this->guessTemplateLine($trace);
+                }
+
+                if (null === $filename) {
+                    $filename = $trace['object']->getTemplateName();
+                }
+            }
+        }
+
+        $this->lineno = $lineno;
+        $this->filename = $filename;
+        $this->rawMessage = $message;
+
+        $this->updateRepr();
+
+        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+            $this->previous = $previous;
+            parent::__construct($this->message);
+        } else {
+            parent::__construct($this->message, 0, $previous);
+        }
+    }
+
+    /**
+     * Gets the raw message.
+     *
+     * @return string The raw message
+     */
+    public function getRawMessage()
+    {
+        return $this->rawMessage;
+    }
+
+    /**
+     * Gets the filename where the error occurred.
+     *
+     * @return string The filename
+     */
+    public function getTemplateFile()
+    {
+        return $this->filename;
+    }
+
+    /**
+     * Sets the filename where the error occurred.
+     *
+     * @param string $filename The filename
+     */
+    public function setTemplateFile($filename)
+    {
+        $this->filename = $filename;
+
+        $this->updateRepr();
+    }
+
+    /**
+     * Gets the template line where the error occurred.
+     *
+     * @return integer The template line
+     */
+    public function getTemplateLine()
+    {
+        return $this->lineno;
+    }
+
+    /**
+     * Sets the template line where the error occurred.
+     *
+     * @param integer $lineno The template line
+     */
+    public function setTemplateLine($lineno)
+    {
+        $this->lineno = $lineno;
+
+        $this->updateRepr();
+    }
+
+    /**
+     * For PHP < 5.3.0, provides access to the getPrevious() method.
+     *
+     * @param  string $method    The method name
+     * @param  array  $arguments The parameters to be passed to the method
+     *
+     * @return Exception The previous exception or null
+     */
+    public function __call($method, $arguments)
+    {
+        if ('getprevious' == strtolower($method)) {
+            return $this->previous;
+        }
+
+        throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
+    }
+
+    protected function updateRepr()
+    {
+        $this->message = $this->rawMessage;
+
+        $dot = false;
+        if ('.' === substr($this->message, -1)) {
+            $this->message = substr($this->message, 0, -1);
+            $dot = true;
+        }
+
+        if (null !== $this->filename) {
+            $this->message .= sprintf(' in %s', is_string($this->filename) ? '"'.$this->filename.'"' : json_encode($this->filename));
+        }
+
+        if ($this->lineno >= 0) {
+            $this->message .= sprintf(' at line %d', $this->lineno);
+        }
+
+        if ($dot) {
+            $this->message .= '.';
+        }
+    }
+
+    protected function getTemplateTrace()
+    {
+        foreach (debug_backtrace() as $trace) {
+            if (isset($trace['object']) && $trace['object'] instanceof Twig_Template) {
+                return $trace;
+            }
+        }
+    }
+
+    protected function guessTemplateLine($trace)
+    {
+        if (isset($trace['line'])) {
+            foreach ($trace['object']->getDebugInfo() as $codeLine => $templateLine) {
+                if ($codeLine <= $trace['line']) {
+                    return $templateLine;
+                }
+            }
+        }
+
+        return -1;
+    }
+}

+ 20 - 0
lib/twig/lib/Twig/Error/Loader.php

@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Exception thrown when an error occurs during template loading.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Error_Loader extends Twig_Error
+{
+}

+ 21 - 0
lib/twig/lib/Twig/Error/Runtime.php

@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ * (c) 2009 Armin Ronacher
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Exception thrown when an error occurs at runtime.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien@symfony.com>
+ */
+class Twig_Error_Runtime extends Twig_Error
+{
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff