index : license-api.git

ascending towards madness

author holly sparkles <sparkles@holly.sh> 2018-06-05 9:09:59.0 +00:00:00
committer holly sparkles <sparkles@holly.sh> 2018-06-05 9:09:59.0 +00:00:00
commit
e308c296947eea2073344def52b5c91c6e88bfa0 [patch]
tree
4ad9b7abc2c3dc775d83c6f83b5368441cb0d8b8
download
e308c296947eea2073344def52b5c91c6e88bfa0.tar.gz

Initial commit



Diff

 .htaccess                                       |    9 +-
 docs/fonts/Roboto/Roboto-Regular-demo.html      |  372 +++-
 docs/fonts/Roboto/Roboto-Regular-webfont.eot    | Bin 0 -> 34722 bytes
 docs/fonts/Roboto/Roboto-Regular-webfont.svg    |  635 ++++-
 docs/fonts/Roboto/Roboto-Regular-webfont.ttf    | Bin 0 -> 34524 bytes
 docs/fonts/Roboto/Roboto-Regular-webfont.woff   | Bin 0 -> 20924 bytes
 docs/fonts/Roboto/stylesheet.css                |   13 +-
 docs/index.php                                  |   23 +-
 docs/stylesheet-dark.css                        |  433 +++-
 docs/stylesheet.css                             |  433 +++-
 errorpages/403.php                              |   18 +-
 errorpages/404.php                              |   18 +-
 errorpages/error-403.md                         |    2 +-
 errorpages/error-404.md                         |    2 +-
 functions.php                                   |  133 +-
 index.php                                       |   53 +-
 licenses/endpoints.txt                          |   15 +-
 licenses/licenses.txt                           |   34 +-
 licenses/raw/afl-3.0.txt                        |  170 +-
 licenses/raw/agpl-3.0.txt                       |  661 +++++-
 licenses/raw/apache-2.0.txt                     |  201 +-
 licenses/raw/artistic-2.0.txt                   |  201 +-
 licenses/raw/bsd-2-clause.txt                   |   25 +-
 licenses/raw/bsd-3-clause-clear.txt             |   32 +-
 licenses/raw/bsd-3-clause.txt                   |   29 +-
 licenses/raw/bsl-1.0.txt                        |   23 +-
 licenses/raw/cc-by-4.0.txt                      |  395 +++-
 licenses/raw/cc-by-sa-4.0.txt                   |  427 +++-
 licenses/raw/cc0-1.0.txt                        |  116 +-
 licenses/raw/ecl-2.0.txt                        |  203 +-
 licenses/raw/epl-1.0.txt                        |  204 +-
 licenses/raw/epl-2.0.txt                        |  277 ++-
 licenses/raw/eupl-1.1.txt                       |  311 ++-
 licenses/raw/eupl-1.2.txt                       |  274 ++-
 licenses/raw/gpl-2.0.txt                        |  339 ++-
 licenses/raw/gpl-3.0.txt                        |  674 +++++-
 licenses/raw/isc.txt                            |   15 +-
 licenses/raw/lgpl-2.1.txt                       |  504 ++++-
 licenses/raw/lgpl-3.0.txt                       |  165 +-
 licenses/raw/lppl-1.3c.txt                      |  415 +++-
 licenses/raw/mit.txt                            |   21 +-
 licenses/raw/mpl-2.0.txt                        |  373 +++-
 licenses/raw/ms-pl.txt                          |   52 +-
 licenses/raw/ms-rl.txt                          |   64 +-
 licenses/raw/ncsa.txt                           |   34 +-
 licenses/raw/ofl-1.1.txt                        |   92 +-
 licenses/raw/osl-3.0.txt                        |  172 +-
 licenses/raw/postgresql.txt                     |   19 +-
 licenses/raw/unlicense.txt                      |   24 +-
 licenses/raw/upl-1.0.txt                        |   35 +-
 licenses/raw/wtfpl.txt                          |   13 +-
 licenses/raw/zlib.txt                           |   19 +-
 vendor/fatfree-3.6/INSTALL.md                   |    0
 vendor/fatfree-3.6/composer.json                |   18 +-
 vendor/fatfree-3.6/config.ini                   |    4 +-
 vendor/fatfree-3.6/index.php                    |   86 +-
 vendor/fatfree-3.6/lib/CHANGELOG.md             |  872 ++++++-
 vendor/fatfree-3.6/lib/COPYING                  |  621 ++++-
 vendor/fatfree-3.6/lib/audit.php                |  191 +-
 vendor/fatfree-3.6/lib/auth.php                 |  249 ++-
 vendor/fatfree-3.6/lib/base.php                 | 3453 ++++++++++++++++++++++++-
 vendor/fatfree-3.6/lib/basket.php               |  238 ++-
 vendor/fatfree-3.6/lib/bcrypt.php               |   96 +-
 vendor/fatfree-3.6/lib/cli/ws.php               |  516 ++++-
 vendor/fatfree-3.6/lib/code.css                 |    1 +-
 vendor/fatfree-3.6/lib/db/cursor.php            |  388 +++-
 vendor/fatfree-3.6/lib/db/jig.php               |  175 +-
 vendor/fatfree-3.6/lib/db/jig/mapper.php        |  538 ++++-
 vendor/fatfree-3.6/lib/db/jig/session.php       |  190 +-
 vendor/fatfree-3.6/lib/db/mongo.php             |  145 +-
 vendor/fatfree-3.6/lib/db/mongo/mapper.php      |  399 +++-
 vendor/fatfree-3.6/lib/db/mongo/session.php     |  190 +-
 vendor/fatfree-3.6/lib/db/sql.php               |  523 ++++-
 vendor/fatfree-3.6/lib/db/sql/mapper.php        |  672 +++++-
 vendor/fatfree-3.6/lib/db/sql/session.php       |  217 ++-
 vendor/fatfree-3.6/lib/f3.php                   |   42 +-
 vendor/fatfree-3.6/lib/image.php                |  616 ++++-
 vendor/fatfree-3.6/lib/log.php                  |   67 +-
 vendor/fatfree-3.6/lib/magic.php                |  139 +-
 vendor/fatfree-3.6/lib/markdown.php             |  572 ++++-
 vendor/fatfree-3.6/lib/matrix.php               |  111 +-
 vendor/fatfree-3.6/lib/session.php              |  192 +-
 vendor/fatfree-3.6/lib/smtp.php                 |  353 ++-
 vendor/fatfree-3.6/lib/template.php             |  353 ++-
 vendor/fatfree-3.6/lib/test.php                 |   96 +-
 vendor/fatfree-3.6/lib/utf.php                  |  199 +-
 vendor/fatfree-3.6/lib/web.php                  |  932 ++++++-
 vendor/fatfree-3.6/lib/web/geo.php              |  108 +-
 vendor/fatfree-3.6/lib/web/google/recaptcha.php |   58 +-
 vendor/fatfree-3.6/lib/web/google/staticmap.php |   65 +-
 vendor/fatfree-3.6/lib/web/oauth2.php           |  136 +-
 vendor/fatfree-3.6/lib/web/openid.php           |  248 ++-
 vendor/fatfree-3.6/lib/web/pingback.php         |  176 +-
 vendor/fatfree-3.6/readme.md                    | 2599 ++++++++++++++++++-
 vendor/fatfree-3.6/ui/css/base.css              |    6 +-
 vendor/fatfree-3.6/ui/css/theme.css             |  165 +-
 vendor/fatfree-3.6/ui/images/bitcoin.png        | Bin 0 -> 5939 bytes
 vendor/fatfree-3.6/ui/images/logo.png           | Bin 0 -> 9895 bytes
 vendor/fatfree-3.6/ui/images/paypal.png         | Bin 0 -> 3127 bytes
 vendor/fatfree-3.6/ui/images/twitter.png        | Bin 0 -> 7675 bytes
 vendor/fatfree-3.6/ui/layout.htm                |   14 +-
 vendor/fatfree-3.6/ui/userref.htm               |    4 +-
 vendor/fatfree-3.6/ui/welcome.htm               |   61 +-
 vendor/parsedown/LICENSE.txt                    |   20 +-
 vendor/parsedown/parsedown.php                  | 1966 ++++++++++++++-
 105 files changed, 27852 insertions(+)

diff --git a/.htaccess b/.htaccess
new file mode 100755
index 0000000..f13f7f2
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,9 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule .* index.php [L,QSA]

Options -Indexes
ErrorDocument 403 /errorpages/403.php
ErrorDocument 404 /errorpages/404.php
diff --git a/docs/fonts/Roboto/Roboto-Regular-demo.html b/docs/fonts/Roboto/Roboto-Regular-demo.html
new file mode 100755
index 0000000..c0204ac
--- /dev/null
+++ b/docs/fonts/Roboto/Roboto-Regular-demo.html
@@ -0,0 +1,372 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
	(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;}
	if(typeof param.defaultContent=="number")
	{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;}
	$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();}
	function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none")
	{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}}
	$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery);
	</script>
	<link rel="stylesheet" href="specimen_files/specimen_stylesheet.css" type="text/css" charset="utf-8" />
	<link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" />

	<style type="text/css">
			body{
			font-family: 'robotoregular';
					}
</style>

<title>Roboto Regular Specimen</title>
	
	
	<script type="text/javascript" charset="utf-8">
		$(document).ready(function() {
			$('#container').easyTabs({defaultContent:1});
		});
	</script>
</head>

<body>
<div id="container">
	<div id="header">
		Roboto Regular	</div>
	<ul class="tabs">
		<li><a href="#specimen">Specimen</a></li>
		<li><a href="#layout">Sample Layout</a></li>
		<li><a href="#installing">Installing Webfonts</a></li>
		
	</ul>
	
	<div id="main_content">

		
			<div id="specimen">
		
				<div class="section">
					<div class="grid12 firstcol">
						<div class="huge">AaBb</div>
					</div>
				</div>
		
				<div class="section">
					<div class="glyph_range">A&#x200B;B&#x200b;C&#x200b;D&#x200b;E&#x200b;F&#x200b;G&#x200b;H&#x200b;I&#x200b;J&#x200b;K&#x200b;L&#x200b;M&#x200b;N&#x200b;O&#x200b;P&#x200b;Q&#x200b;R&#x200b;S&#x200b;T&#x200b;U&#x200b;V&#x200b;W&#x200b;X&#x200b;Y&#x200b;Z&#x200b;a&#x200b;b&#x200b;c&#x200b;d&#x200b;e&#x200b;f&#x200b;g&#x200b;h&#x200b;i&#x200b;j&#x200b;k&#x200b;l&#x200b;m&#x200b;n&#x200b;o&#x200b;p&#x200b;q&#x200b;r&#x200b;s&#x200b;t&#x200b;u&#x200b;v&#x200b;w&#x200b;x&#x200b;y&#x200b;z&#x200b;1&#x200b;2&#x200b;3&#x200b;4&#x200b;5&#x200b;6&#x200b;7&#x200b;8&#x200b;9&#x200b;0&#x200b;&amp;&#x200b;.&#x200b;,&#x200b;?&#x200b;!&#x200b;&#64;&#x200b;(&#x200b;)&#x200b;#&#x200b;$&#x200b;%&#x200b;*&#x200b;+&#x200b;-&#x200b;=&#x200b;:&#x200b;;</div>
				</div>
				<div class="section">
					<div class="grid12 firstcol">
						<table class="sample_table">
							<tr><td>10</td><td class="size10">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>11</td><td class="size11">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>12</td><td class="size12">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>13</td><td class="size13">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>14</td><td class="size14">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>16</td><td class="size16">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>18</td><td class="size18">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>20</td><td class="size20">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>24</td><td class="size24">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>30</td><td class="size30">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>36</td><td class="size36">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>48</td><td class="size48">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>60</td><td class="size60">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>72</td><td class="size72">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
							<tr><td>90</td><td class="size90">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
						</table>
				
					</div>
			
				</div>
		
		
		
								<div class="section" id="bodycomparison">


										<div id="xheight">
				<div class="fontbody">&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;body</div><div class="arialbody">body</div><div class="verdanabody">body</div><div class="georgiabody">body</div></div>
										<div class="fontbody" style="z-index:1">
											body<span>Roboto Regular</span>
										</div>
										<div class="arialbody" style="z-index:1">
											body<span>Arial</span>
										</div>
										<div class="verdanabody" style="z-index:1">
											body<span>Verdana</span>
										</div>
										<div class="georgiabody" style="z-index:1">
											body<span>Georgia</span>
										</div>



								</div>
		
		
				<div class="section psample psample_row1" id="">
					
					<div class="grid2 firstcol">
						<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid3">
						<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid3">
						<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid4">
						<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="white_blend"></div>
					
				</div>
				<div class="section psample psample_row2" id="">
					<div class="grid3 firstcol">
						<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid4">
						<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid5">
						<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>

					<div class="white_blend"></div>

				</div>
				
				<div class="section psample psample_row3" id="">
					<div class="grid5 firstcol">
						<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
					</div>
					<div class="grid7">
						<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
					</div>
					
					<div class="white_blend"></div>
					
				</div>
				
				<div class="section psample psample_row4" id="">
					<div class="grid12 firstcol">
						<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
					</div>
					<div class="white_blend"></div>
					
				</div>
				
				
				
				<div class="section psample psample_row1 fullreverse">
					<div class="grid2 firstcol">
						<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid3">
						<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid3">
						<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid4">
						<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="black_blend"></div>
					
				</div>
				
				<div class="section psample psample_row2 fullreverse">
					<div class="grid3 firstcol">
						<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid4">
						<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="grid5">
						<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
			
					</div>
					<div class="black_blend"></div>

				</div>
				
				<div class="section psample fullreverse psample_row3" id="">
					<div class="grid5 firstcol">
						<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
					</div>
					<div class="grid7">
						<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
					</div>
					
					<div class="black_blend"></div>
					
				</div>
				
				<div class="section psample fullreverse psample_row4" id="" style="border-bottom: 20px #000 solid;">
					<div class="grid12 firstcol">
						<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
					</div>
					<div class="black_blend"></div>
					
				</div>
				
				
				
				
			</div>
			
			<div id="layout">
				
				<div class="section">
					
					<div class="grid12 firstcol">
						<h1>Lorem Ipsum Dolor</h1>
						<h2>Etiam porta sem malesuada magna mollis euismod</h2>
						
						<p class="byline">By <a href="#link">Aenean Lacinia</a></p>
					</div>
				</div>
				<div class="section">
					<div class="grid8 firstcol">
						<p class="large">Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>

						
						<h3>Pellentesque ornare sem</h3>

						<p>Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit. </p>

						<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. </p>

						<p>Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur. </p>

						<p>Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla. </p>

						<h3>Cras mattis consectetur</h3>

						<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum. </p>

						<p>Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.</p>
					</div>
					
					<div class="grid4 sidebar">
						
						<div class="box reverse">
							<p class="last">Nullam quis risus eget urna mollis ornare vel eu leo. Donec ullamcorper nulla non metus auctor fringilla. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>
						</div>
						
						<p class="caption">Maecenas sed diam eget risus varius.</p>

						<p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>

					

						<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Aenean lacinia bibendum nulla sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean lacinia bibendum nulla sed consectetur. Nullam quis risus eget urna mollis ornare vel eu leo. </p>

						<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec ullamcorper nulla non metus auctor fringilla. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>

					</div>
				</div>
				
			</div>







		
		
		<div id="specs">
			
		</div>
	
		<div id="installing">
			<div class="section">
				<div class="grid7 firstcol">
					<h1>Installing Webfonts</h1>
					
					<p>Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.</p>
					
					<h2>1. Upload your webfonts</h2>
					<p>You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.</p>
					
					<h2>2. Include the webfont stylesheet</h2>
					<p>A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the <a href="https://www.fontspring.com/blog/further-hardening-of-the-bulletproof-syntax">Fontspring blog post</a> about it. The code for it is as follows:</p>
					
					
<code>
@font-face{ 
	font-family: 'MyWebFont';
	src: url('WebFont.eot');
	src: url('WebFont.eot?iefix') format('eot'),
	     url('WebFont.woff') format('woff'),
	     url('WebFont.ttf') format('truetype'),
	     url('WebFont.svg#webfont') format('svg');
}
</code>

	<p>We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:</p>
	<code>&lt;link rel=&quot;stylesheet&quot; href=&quot;stylesheet.css&quot; type=&quot;text/css&quot; charset=&quot;utf-8&quot; /&gt;</code>

					<h2>3. Modify your own stylesheet</h2>
					<p>To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:</p>
<code>p { font-family: 'MyWebFont', Arial, sans-serif; }</code>

<h2>4. Test</h2>
<p>Getting webfonts to work cross-browser <em>can</em> be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.</p>
				</div>
				
				<div class="grid5 sidebar">
					<div class="box">
						<h2>Troubleshooting<br />Font-Face Problems</h2>
						<p>Having trouble getting your webfonts to load in your new website? Here are some tips to sort out what might be the problem.</p>

						<h3>Fonts not showing in any browser</h3>

						<p>This sounds like you need to work on the plumbing. You either did not upload the fonts to the correct directory, or you did not link the fonts properly in the CSS. If you've confirmed that all this is correct and you still have a problem, take a look at your .htaccess file and see if requests are getting intercepted.</p>

						<h3>Fonts not loading in iPhone or iPad</h3>

						<p>The most common problem here is that you are serving the fonts from an IIS server. IIS refuses to serve files that have unknown MIME types. If that is the case, you must set the MIME type for SVG to "image/svg+xml" in the server settings. Follow these instructions from Microsoft if you need help.</p>

						<h3>Fonts not loading in Firefox</h3>

						<p>The primary reason for this failure? You are still using a version Firefox older than 3.5. So upgrade already! If that isn't it, then you are very likely serving fonts from a different domain. Firefox requires that all font assets be served from the same domain. Lastly it is possible that you need to add WOFF to your list of MIME types (if you are serving via IIS.)</p>

						<h3>Fonts not loading in IE</h3>

						<p>Are you looking at Internet Explorer on an actual Windows machine or are you cheating by using a service like Adobe BrowserLab? Many of these screenshot services do not render @font-face for IE. Best to test it on a real machine.</p>

						<h3>Fonts not loading in IE9</h3>

						<p>IE9, like Firefox, requires that fonts be served from the same domain as the website. Make sure that is the case.</p>
					</div>
				</div>
			</div>
			
		</div>
	
	</div>
	<div id="footer">
		<p>&copy;2010-2011 Fontspring. All rights reserved.</p>
	</div>
</div>
</body>
</html>
diff --git a/docs/fonts/Roboto/Roboto-Regular-webfont.eot b/docs/fonts/Roboto/Roboto-Regular-webfont.eot
new file mode 100755
index 0000000..c42f240
Binary files /dev/null and b/docs/fonts/Roboto/Roboto-Regular-webfont.eot differ
diff --git a/docs/fonts/Roboto/Roboto-Regular-webfont.svg b/docs/fonts/Roboto/Roboto-Regular-webfont.svg
new file mode 100755
index 0000000..57a15c6
--- /dev/null
+++ b/docs/fonts/Roboto/Roboto-Regular-webfont.svg
@@ -0,0 +1,635 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<metadata></metadata>
<defs>
<font id="robotoregular" horiz-adv-x="1164" >
<font-face units-per-em="2048" ascent="1638" descent="-410" />
<missing-glyph horiz-adv-x="509" />
<glyph unicode="&#xfb01;" horiz-adv-x="1140" d="M198 0v936h-170v146h170v117q0 182 106.5 282t295.5 100q67 0 132 -15.5t153 -45.5l-34 -160q-53 21 -113 36t-123 15q-117 0 -168.5 -52t-51.5 -160v-117h215v-146h-215v-936h-197zM981 0h-198v1082h198v-1082z" />
<glyph horiz-adv-x="0" />
<glyph unicode="&#xd;" horiz-adv-x="509" />
<glyph unicode=" "  horiz-adv-x="509" />
<glyph unicode="&#x09;" horiz-adv-x="509" />
<glyph unicode="&#xa0;" horiz-adv-x="509" />
<glyph unicode="!" horiz-adv-x="539" d="M368 478h-197v978h197v-978zM369 0h-198v204h198v-204z" />
<glyph unicode="&#x22;" horiz-adv-x="668" d="M278 1290l-101 -250h-97l1 240v280h197v-270zM587 1290l-101 -250h-97l1 248v272h197v-270z" />
<glyph unicode="#" horiz-adv-x="1276" d="M712 410h-255l-80 -410h-151l80 410h-236v140h264l68 348h-256v142h284l82 416h151l-82 -416h255l82 416h151l-82 -416h199v-142h-226l-68 -348h219v-140h-247l-80 -410h-152zM485 550h255l68 348h-255z" />
<glyph unicode="$" horiz-adv-x="1153" d="M848 376q0 89 -64 153t-210 114q-202 61 -305 163t-103 272q0 165 94.5 269t260.5 125v221h158v-222q168 -24 260.5 -143.5t92.5 -320.5h-196q0 136 -63 220t-175 84q-118 0 -176.5 -61.5t-58.5 -168.5q0 -97 60.5 -157t218.5 -114q205 -66 304 -164.5t99 -267.5 q0 -172 -103 -273.5t-283 -120.5v-192h-157v191q-172 18 -282 125.5t-106 315.5l2 5h190q0 -154 77.5 -219.5t190.5 -65.5q129 0 201.5 61.5t72.5 170.5z" />
<glyph unicode="%" horiz-adv-x="1498" d="M104 1176q0 127 82 214t219 87t219 -86.5t82 -214.5v-77q0 -127 -81.5 -213t-217.5 -86q-138 0 -220.5 86t-82.5 213v77zM250 1099q0 -74 40.5 -125.5t116.5 -51.5q73 0 113 51t40 126v77q0 74 -40.5 126.5t-114.5 52.5q-75 0 -115 -52.5t-40 -126.5v-77zM809 357 q0 127 82 213.5t219 86.5q136 0 218.5 -86.5t82.5 -213.5v-78q0 -128 -82 -214t-217 -86q-138 0 -220.5 86t-82.5 214v78zM955 279q0 -75 40.5 -126.5t116.5 -51.5q73 0 113 51.5t40 126.5v78q0 74 -41 126t-114 52q-74 0 -114.5 -52t-40.5 -126v-78zM458 110l-109 67 l711 1138l109 -67z" />
<glyph unicode="&#x26;" horiz-adv-x="1276" d="M64 392q0 122 70.5 213.5t210.5 183.5q-78 99 -116 176.5t-38 159.5q0 169 97.5 260.5t268.5 91.5q158 0 257 -91t99 -219q0 -98 -52.5 -169.5t-155.5 -146.5l-109 -80l340 -409q41 65 64 144t23 167h176q0 -132 -39 -244t-113 -201l185 -223l-2 -5h-229l-85 102 q-80 -60 -177 -91.5t-201 -31.5q-217 0 -345.5 115t-128.5 298zM538 133q72 0 142 24.5t132 70.5l-361 435l-40 -29q-91 -68 -120.5 -130t-29.5 -112q0 -113 71 -186t206 -73zM388 1127q0 -53 27 -110.5t81 -125.5l138 95q57 38 77.5 82.5t20.5 98.5q0 61 -48.5 108 t-126.5 47q-81 0 -125 -56.5t-44 -138.5z" />
<glyph unicode="'" horiz-adv-x="359" d="M278 1337l-101 -282h-97l1 265v240h197v-223z" />
<glyph unicode="(" horiz-adv-x="679" d="M132 591q0 394 159 673t334 372l6 -1l38 -116q-137 -107 -238.5 -343t-101.5 -583v-13q0 -347 101 -583t239 -352l-38 -108h-6q-175 93 -334 371.5t-159 673.5v9z" />
<glyph unicode=")" horiz-adv-x="687" d="M543 582q0 -395 -159.5 -673.5t-333.5 -371.5h-6l-38 108q135 105 237.5 345.5t102.5 589.5v13q0 342 -105.5 583.5t-234.5 351.5l38 108h6q174 -93 333.5 -372t159.5 -673v-9z" />
<glyph unicode="*" horiz-adv-x="884" d="M324 982l-296 89l49 154l296 -111l-10 342h161l-10 -348l293 110l48 -156l-302 -89l193 -270l-131 -96l-181 287l-176 -279l-132 93z" />
<glyph unicode="+" horiz-adv-x="1162" d="M677 783h399v-178h-399v-459h-197v459h-402v178h402v423h197v-423z" />
<glyph unicode="," horiz-adv-x="404" d="M315 48l-150 -306h-117l70 316v163h197v-173z" />
<glyph unicode="-" horiz-adv-x="561" d="M525 538h-490v154h490v-154z" />
<glyph unicode="." horiz-adv-x="548" d="M358 0h-197v202h197v-202z" />
<glyph unicode="/" horiz-adv-x="850" d="M184 -125h-168l608 1581h167z" />
<glyph unicode="0" horiz-adv-x="1154" d="M1040 555q0 -279 -123.5 -427.5t-338.5 -148.5t-340 149t-125 427v345q0 278 124.5 427.5t338.5 149.5q215 0 339.5 -149.5t124.5 -427.5v-345zM843 942q0 189 -68.5 284.5t-198.5 95.5t-198 -95.5t-68 -284.5v-427q0 -189 69 -285.5t199 -96.5t197.5 96t67.5 286v427z " />
<glyph unicode="1" horiz-adv-x="1153" d="M681 0h-197v1264l-298 -4v142l495 54v-1456z" />
<glyph unicode="2" horiz-adv-x="1153" d="M1062 0h-944v135l477 530q128 143 173.5 227t45.5 172q0 109 -63.5 183.5t-168.5 74.5q-151 0 -222.5 -77.5t-71.5 -217.5h-189l-2 6q-5 188 125 316t360 128q196 0 312.5 -114.5t116.5 -291.5q0 -119 -70.5 -238.5t-197.5 -256.5l-383 -417l2 -5h700v-154z" />
<glyph unicode="3" horiz-adv-x="1153" d="M388 819h172q131 0 185.5 65.5t54.5 182.5q0 125 -62 190t-183 65q-115 0 -184.5 -67.5t-69.5 -179.5h-189l-2 6q-5 165 119.5 280.5t325.5 115.5q202 0 322 -107.5t120 -306.5q0 -90 -54.5 -179.5t-163.5 -136.5q131 -43 185.5 -135t54.5 -206q0 -199 -130.5 -313 t-333.5 -114q-199 0 -329.5 107.5t-125.5 291.5l3 6h188q0 -115 70.5 -183t193.5 -68q125 0 196 68t71 201q0 135 -63 199t-199 64h-172v154z" />
<glyph unicode="4" horiz-adv-x="1153" d="M905 490h201v-154h-201v-336h-196v336h-654v111l642 1009h208v-966zM265 490h444v683l-6 1l-19 -50z" />
<glyph unicode="5" horiz-adv-x="1153" d="M175 657l84 799h729v-175h-562l-48 -409q46 34 102.5 56.5t130.5 24.5q201 2 316.5 -131t115.5 -358q0 -219 -117.5 -352t-342.5 -133q-185 0 -308 101t-118 297l2 6h178q0 -119 68.5 -184.5t177.5 -65.5q125 0 194 88t69 241q0 140 -70 230t-193 90q-116 0 -168 -35 t-76 -107z" />
<glyph unicode="6" horiz-adv-x="1153" d="M675 1477q75 0 148.5 -17t121.5 -43l-42 -151q-49 25 -102.5 40.5t-125.5 15.5q-156 0 -251.5 -125t-95.5 -326v-23q64 56 146.5 87.5t177.5 31.5q195 0 311 -135t116 -342q0 -226 -123.5 -368.5t-329.5 -142.5q-214 0 -354 155t-140 437v278q0 280 156 454t387 174z M608 820q-101 0 -172 -41t-108 -109v-118q0 -201 85 -310t213 -109q121 0 188.5 102.5t67.5 254.5q0 144 -72.5 237t-201.5 93z" />
<glyph unicode="7" horiz-adv-x="1153" d="M1062 1301q-264 -314 -356.5 -556.5t-133.5 -587.5l-16 -157h-197l16 157q42 344 163 615t331 529h-792v155h985v-155z" />
<glyph unicode="8" horiz-adv-x="1153" d="M1010 1076q0 -114 -64 -199.5t-173 -127.5q126 -44 201.5 -138t75.5 -217q0 -202 -131.5 -308.5t-341.5 -106.5q-214 0 -344.5 106.5t-130.5 308.5q0 123 74 217t200 138q-109 42 -171 127.5t-62 199.5q0 192 118.5 296.5t313.5 104.5q192 0 313.5 -104.5t121.5 -296.5z M854 398q0 120 -79 197t-200 77q-123 0 -199.5 -77t-76.5 -197q0 -124 76 -194.5t202 -70.5q123 0 200 71t77 194zM813 1072q0 108 -67.5 179t-170.5 71q-105 0 -170 -68.5t-65 -181.5q0 -111 65.5 -178t171.5 -67q104 0 170 67t66 178z" />
<glyph unicode="9" horiz-adv-x="1153" d="M511 133q144 0 230.5 109t86.5 324v66q-49 -71 -122.5 -107.5t-163.5 -36.5q-211 0 -335 130.5t-124 359.5q0 219 131.5 359t319.5 140q228 0 359.5 -142.5t131.5 -419.5v-347q0 -285 -142.5 -437t-371.5 -152q-77 0 -156.5 14.5t-142.5 44.5l30 151q59 -31 122.5 -43.5 t146.5 -12.5zM542 642q109 0 181.5 47t104.5 120v126q0 191 -73.5 289t-214.5 98q-108 0 -184 -96.5t-76 -247.5q0 -150 70.5 -243t191.5 -93z" />
<glyph unicode=":" horiz-adv-x="517" d="M358 0h-197v202h197v-202zM358 876h-197v202h197v-202z" />
<glyph unicode=";" horiz-adv-x="525" d="M359 876h-197v202h197v-202zM366 48l-150 -306h-117l70 316v163h197v-173z" />
<glyph unicode="&#x3c;" horiz-adv-x="1040" d="M328 559l-85 -18v-6l85 -19l559 -228v-201l-816 379v149l816 378v-201z" />
<glyph unicode="=" horiz-adv-x="1153" d="M986 823h-834v164h834v-164zM986 407h-834v164h834v-164z" />
<glyph unicode="&#x3e;" horiz-adv-x="1072" d="M136 798v195l856 -378v-149l-856 -379v196l598 238l85 17v6l-85 20z" />
<glyph unicode="?" horiz-adv-x="974" d="M355 410q1 145 25 201t126 148q99 117 141 180t42 152q0 106 -56.5 163t-161.5 57q-91 0 -155 -49.5t-64 -145.5h-188l-3 6q-3 161 113.5 258t296.5 97q197 0 306 -100.5t109 -280.5q0 -129 -70.5 -236t-186.5 -219q-54 -54 -65.5 -97t-11.5 -134h-197zM559 0h-206v208 h206v-208z" />
<glyph unicode="@" horiz-adv-x="1833" d="M1732 502q-9 -214 -120 -368.5t-332 -154.5q-73 0 -126 41.5t-76 117.5q-50 -80 -122 -119.5t-168 -39.5q-125 0 -194 120.5t-51 316.5q23 259 137.5 415.5t279.5 156.5q105 0 169 -26t139 -80l-4 -4h6l-51 -585q-9 -110 21.5 -151.5t81.5 -41.5q123 0 197 113.5 t82 288.5q16 382 -144 595.5t-496 213.5q-308 0 -495.5 -231t-202.5 -602q-18 -376 150 -594.5t482 -218.5q88 0 178.5 21.5t152.5 56.5l38 -107q-67 -42 -170.5 -65.5t-202.5 -23.5q-380 0 -587.5 249.5t-189.5 681.5q19 423 249 688t602 265q379 0 581.5 -250t185.5 -679z M720 416q-11 -142 21.5 -216t106.5 -74q64 0 117 24.5t97 87.5q-1 12 -0.5 25.5t2.5 29.5l47 538q-26 12 -54.5 19t-59.5 7q-125 0 -191 -109.5t-86 -331.5z" />
<glyph unicode="A" horiz-adv-x="1326" d="M969 375h-610l-138 -375h-201l563 1456h169l554 -1456h-201zM420 540h490l-240 663h-6z" />
<glyph unicode="B" horiz-adv-x="1309" d="M180 0v1456h475q228 0 357 -98.5t129 -295.5q0 -97 -62 -173.5t-163 -113.5q132 -28 207.5 -129t75.5 -235q0 -200 -129.5 -305.5t-351.5 -105.5h-538zM377 683v-529h341q134 0 209 66.5t75 188.5q0 128 -62.5 201t-192.5 73h-370zM377 837h319q110 0 179 60.5t69 168.5 q0 118 -74.5 176.5t-214.5 58.5h-278v-464z" />
<glyph unicode="C" horiz-adv-x="1297" d="M1209 466l2 -6q4 -205 -144 -343t-391 -138q-247 0 -402.5 175t-155.5 444v259q0 269 155.5 444.5t402.5 175.5t393 -131.5t142 -348.5l-2 -6h-189q0 153 -90 242t-254 89q-165 0 -263 -133t-98 -330v-261q0 -199 98 -332t263 -133q164 0 254 88.5t90 244.5h189z" />
<glyph unicode="D" horiz-adv-x="1349" d="M180 0v1456h447q286 0 459 -175.5t173 -453.5v-199q0 -279 -173 -453.5t-459 -174.5h-447zM377 1301v-1147h250q202 0 318.5 133t116.5 341v201q0 206 -116.5 339t-318.5 133h-250z" />
<glyph unicode="E" horiz-adv-x="1197" d="M1044 675h-667v-521h769v-154h-966v1456h955v-155h-758v-471h667v-155z" />
<glyph unicode="F" horiz-adv-x="1193" d="M1041 644h-664v-644h-197v1456h963v-155h-766v-502h664v-155z" />
<glyph unicode="G" horiz-adv-x="1396" d="M1235 193q-52 -80 -180.5 -147t-332.5 -67q-261 0 -431.5 167t-170.5 432v300q0 265 159 432t410 167q250 0 393 -123t146 -317l-2 -6h-188q-9 127 -96.5 209t-252.5 82q-167 0 -269 -125t-102 -317v-302q0 -194 114 -319.5t290 -125.5q124 0 203 33t113 75v331h-319v155 h516v-534z" />
<glyph unicode="H" horiz-adv-x="1461" d="M1281 0h-197v643h-707v-643h-197v1456h197v-658h707v658h197v-1456z" />
<glyph unicode="I" horiz-adv-x="579" d="M388 0h-198v1456h198v-1456z" />
<glyph unicode="J" horiz-adv-x="1130" d="M766 1456h197v-1053q0 -195 -123.5 -309.5t-320.5 -114.5q-210 0 -334 106.5t-119 309.5l2 6h189q0 -135 68.5 -201.5t193.5 -66.5q109 0 178 73.5t69 196.5v1053z" />
<glyph unicode="K" horiz-adv-x="1317" d="M547 657h-170v-657h-197v1456h197v-644h152l521 644h218l3 -5l-565 -699l606 -747l-3 -5h-235z" />
<glyph unicode="L" horiz-adv-x="1106" d="M377 154h689v-154h-886v1456h197v-1302z" />
<glyph unicode="M" horiz-adv-x="1799" d="M432 1456l464 -1183h6l464 1183h252v-1456h-197v576l20 592l-5 1l-472 -1169h-131l-470 1166l-5 -1l19 -589v-576h-197v1456h252z" />
<glyph unicode="N" horiz-adv-x="1461" d="M1281 0h-197l-701 1126l-6 -2v-1124h-197v1456h197l701 -1124l6 2v1122h197v-1456z" />
<glyph unicode="O" horiz-adv-x="1396" d="M1282 598q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259zM1085 859q0 200 -108 328t-290 128q-172 0 -274.5 -128t-102.5 -328v-261q0 -202 102.5 -330t274.5 -128 q183 0 290.5 127.5t107.5 330.5v261z" />
<glyph unicode="P" horiz-adv-x="1312" d="M377 584v-584h-197v1456h557q233 0 362 -120t129 -316q0 -199 -129 -317.5t-362 -118.5h-360zM377 738h360q148 0 221 79.5t73 200.5t-73.5 202t-220.5 81h-360v-563z" />
<glyph unicode="Q" horiz-adv-x="1396" d="M1282 598q0 -142 -50 -263t-140 -205l247 -233l-135 -129l-276 257q-56 -23 -116.5 -34.5t-124.5 -11.5q-255 0 -414.5 176t-159.5 443v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259zM1085 859q0 200 -108 328t-290 128q-172 0 -274.5 -128 t-102.5 -328v-261q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5t107.5 330.5v261z" />
<glyph unicode="R" horiz-adv-x="1357" d="M377 631v-631h-197v1455h527q239 0 365 -106t126 -308q0 -112 -58.5 -195t-170.5 -132q120 -39 172.5 -126.5t52.5 -216.5v-137q0 -68 15 -122t52 -88v-24h-203q-39 34 -50 100t-11 136v133q0 118 -69 190t-185 72h-366zM377 786h310q167 0 240.5 63.5t73.5 193.5 q0 123 -71.5 190.5t-222.5 67.5h-330v-515z" />
<glyph unicode="S" horiz-adv-x="1277" d="M973 367q0 100 -75 167.5t-266 113.5q-231 55 -360.5 162t-129.5 269q0 170 139.5 284t361.5 114q239 0 380.5 -131t136.5 -304l-2 -6h-188q0 128 -84.5 207t-242.5 79q-147 0 -225.5 -66.5t-78.5 -173.5q0 -95 85 -158.5t276 -111.5q230 -57 350 -168t120 -275 q0 -176 -144 -283t-376 -107q-218 0 -385.5 118t-162.5 316l2 6h188q0 -140 103 -213t255 -73q149 0 236 63t87 171z" />
<glyph unicode="T" horiz-adv-x="1200" d="M1166 1301h-468v-1301h-197v1301h-467v155h1132v-155z" />
<glyph unicode="U" horiz-adv-x="1386" d="M1244 1456v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197z" />
<glyph unicode="V" horiz-adv-x="1295" d="M612 362l33 -115h6l33 115l376 1094h213l-541 -1456h-169l-541 1456h214z" />
<glyph unicode="W" horiz-adv-x="1809" d="M472 504l27 -182l6 -1l39 183l267 952h174l269 -952l40 -187h6l29 187l217 952h197l-351 -1456h-176l-287 1010l-26 131h-6l-25 -131l-292 -1010h-176l-350 1456h196z" />
<glyph unicode="X" horiz-adv-x="1295" d="M650 888l340 568h238l-462 -722l472 -734h-235l-349 578l-350 -578h-238l472 734l-462 722h236z" />
<glyph unicode="Y" horiz-adv-x="1250" d="M625 716l380 740h225l-511 -944v-512h-196v525l-503 931h225z" />
<glyph unicode="Z" horiz-adv-x="1225" d="M319 154h814v-154h-1036v146l778 1155h-767v155h992v-141z" />
<glyph unicode="[" horiz-adv-x="552" d="M528 1509h-188v-1666h188v-155h-385v1976h385v-155z" />
<glyph unicode="\" horiz-adv-x="846" d="M39 1456h186l608 -1581h-186z" />
<glyph unicode="]" horiz-adv-x="552" d="M11 1664h386v-1976h-386v155h189v1666h-189v155z" />
<glyph unicode="^" horiz-adv-x="856" d="M244 729h-183l299 727h134l298 -727h-181l-166 419l-16 70h-6l-16 -70z" />
<glyph unicode="_" horiz-adv-x="931" d="M927 -154h-923v154h923v-154z" />
<glyph unicode="`" horiz-adv-x="641" d="M490 1211h-158l-250 260l3 6h230z" />
<glyph unicode="a" horiz-adv-x="1126" d="M808 0q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5 t112 -269.5v-521q0 -58 6 -112t22 -106h-203zM481 144q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141q0 -72 45 -114t133 -42z" />
<glyph unicode="b" d="M1067 500q0 -234 -112.5 -377.5t-309.5 -143.5q-107 0 -186 41.5t-131 122.5l-24 -143h-161v1560h197v-606q51 72 126.5 110t176.5 38q200 0 312 -160t112 -421v-21zM870 521q0 186 -68.5 303.5t-208.5 117.5q-91 0 -153.5 -44.5t-99.5 -119.5v-469q38 -80 99.5 -125 t155.5 -45q139 0 207 99t68 262v21z" />
<glyph unicode="c" horiz-adv-x="1087" d="M583 133q98 0 172.5 60.5t74.5 148.5h177l2 -6q5 -140 -124.5 -248.5t-301.5 -108.5q-236 0 -361 154t-125 387v42q0 231 125.5 385.5t360.5 154.5q190 0 310.5 -112t116.5 -275l-2 -6h-178q0 99 -70 168.5t-177 69.5q-155 0 -221.5 -111.5t-66.5 -273.5v-42 q0 -166 66 -276.5t222 -110.5z" />
<glyph unicode="d" d="M98 521q0 261 111.5 421t312.5 160q95 0 168.5 -35t125.5 -102v595h197v-1560h-161l-23 133q-53 -76 -130 -115t-179 -39q-198 0 -310 143.5t-112 377.5v21zM295 500q0 -164 67 -262.5t208 -98.5q88 0 148 40t98 112v505q-38 67 -98.5 106.5t-145.5 39.5 q-142 0 -209.5 -117t-67.5 -304v-21z" />
<glyph unicode="e" horiz-adv-x="1083" d="M592 -21q-226 0 -359.5 150.5t-133.5 390.5v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38zM562 947q-99 0 -169 -83.5t-86 -209.5l2 -5h499 v26q0 116 -62 194t-184 78z" />
<glyph unicode="f" horiz-adv-x="707" d="M225 0v936h-169v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197z" />
<glyph unicode="g" d="M100 521q0 261 114 421t315 160q103 0 181 -41.5t130 -119.5l24 141h157v-1088q0 -208 -121 -319.5t-349 -111.5q-78 0 -168.5 21.5t-159.5 58.5l50 153q53 -30 128 -48.5t148 -18.5q144 0 209.5 65.5t65.5 199.5v122q-53 -68 -127 -102.5t-170 -34.5q-199 0 -313 144 t-114 377v21zM297 500q0 -163 69 -262t210 -99q89 0 149 40.5t99 114.5v498q-38 69 -99 109.5t-147 40.5q-141 0 -211 -118t-70 -303v-21z" />
<glyph unicode="h" d="M340 937q56 78 137.5 121.5t180.5 43.5q173 0 269.5 -104t96.5 -320v-678h-197v680q0 134 -57.5 198t-171.5 64q-82 0 -148.5 -38.5t-109.5 -104.5v-799h-197v1560h197v-623z" />
<glyph unicode="i" horiz-adv-x="516" d="M356 0h-197v1082h197v-1082zM356 1359h-197v201h197v-201z" />
<glyph unicode="j" horiz-adv-x="530" d="M370 1082v-1171q0 -167 -86 -257.5t-239 -90.5q-31 0 -56.5 4.5t-54.5 13.5l14 155q14 -5 40 -8.5t43 -3.5q65 0 103.5 44t38.5 143v1171h197zM364 1363h-197v197h197v-197z" />
<glyph unicode="k" horiz-adv-x="1050" d="M469 499h-128v-499h-197v1560h197v-904h126l296 426h236l-370 -492l423 -590h-232z" />
<glyph unicode="l" horiz-adv-x="516" d="M356 0h-197v1560h197v-1560z" />
<glyph unicode="m" horiz-adv-x="1790" d="M319 1082l14 -142q53 77 134.5 119.5t189.5 42.5t185.5 -50t116.5 -150q51 92 135 146t196 54q165 0 261 -113.5t96 -341.5v-647h-197v649q0 160 -55 226.5t-164 66.5q-101 0 -163.5 -70t-73.5 -177v-8v-687h-198v649q0 152 -56.5 222.5t-162.5 70.5q-90 0 -148 -37 t-89 -104v-801h-197v1082h176z" />
<glyph unicode="n" d="M319 1082l14 -161q54 86 135.5 133.5t185.5 47.5q175 0 271 -102.5t96 -316.5v-683h-197v679q0 143 -56.5 203t-172.5 60q-85 0 -150.5 -41t-104.5 -112v-789h-197v1082h176z" />
<glyph unicode="o" d="M97 551q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5v22zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z" />
<glyph unicode="p" d="M1065 500q0 -234 -112 -377.5t-309 -143.5q-100 0 -175.5 33.5t-128.5 100.5v-529h-197v1498h151l31 -140q53 78 132 119t184 41q201 0 312.5 -159.5t111.5 -421.5v-21zM868 521q0 184 -72.5 302.5t-213.5 118.5q-85 0 -145 -38.5t-97 -105.5v-523q37 -67 97 -104.5 t147 -37.5q140 0 212 102.5t72 264.5v21z" />
<glyph unicode="q" d="M98 521q0 261 111.5 421t312.5 160q99 0 174 -37.5t127 -109.5l29 127h150v-1498h-197v518q-52 -61 -123 -92t-162 -31q-198 0 -310 143.5t-112 377.5v21zM295 500q0 -164 67.5 -265.5t207.5 -101.5q81 0 138.5 36t96.5 101v546q-39 61 -96.5 96t-136.5 35 q-141 0 -209 -119.5t-68 -306.5v-21z" />
<glyph unicode="r" horiz-adv-x="702" d="M655 908l-101 6q-78 0 -131.5 -37t-82.5 -104v-773h-197v1082h176l19 -158q46 84 113.5 131t155.5 47q22 0 42 -3.5t33 -7.5z" />
<glyph unicode="s" horiz-adv-x="1071" d="M772 287q0 65 -49.5 107t-187.5 73q-197 43 -296.5 116.5t-99.5 200.5q0 132 112 225t292 93q189 0 300.5 -97t106.5 -236l-2 -6h-188q0 71 -59.5 127.5t-157.5 56.5q-105 0 -156 -46t-51 -111q0 -64 45 -101t183 -66q205 -44 305 -119.5t100 -202.5q0 -144 -116.5 -233 t-304.5 -89q-207 0 -326 105t-113 245l2 6h188q5 -105 78 -153.5t171 -48.5q105 0 164.5 42.5t59.5 111.5z" />
<glyph unicode="t" horiz-adv-x="708" d="M403 1343v-261h205v-146h-205v-657q0 -76 31.5 -107t83.5 -31q17 0 37.5 4t36.5 10l26 -135q-22 -18 -64.5 -29.5t-85.5 -11.5q-120 0 -191 72.5t-71 227.5v657h-172v146h172v261h197z" />
<glyph unicode="u" d="M830 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177z" />
<glyph unicode="v" horiz-adv-x="1030" d="M504 319l17 -76h6l19 76l249 763h201l-398 -1082h-149l-403 1082h202z" />
<glyph unicode="w" horiz-adv-x="1550" d="M420 394l23 -131h6l28 131l216 688h158l217 -688l31 -146h6l29 146l170 688h196l-314 -1082h-159l-209 659l-45 184l-6 -1l-43 -183l-206 -659h-159l-314 1082h196z" />
<glyph unicode="x" horiz-adv-x="1030" d="M510 683l230 399h230l-351 -535l361 -547h-226l-240 409l-240 -409h-228l361 547l-351 535h227z" />
<glyph unicode="y" horiz-adv-x="1030" d="M474 401l35 -136h6l266 817h219l-455 -1248q-41 -109 -117.5 -190t-206.5 -81q-24 0 -61 5.5t-57 10.5l20 155q-6 1 35.5 -2t52.5 -3q63 0 103 56t67 124l47 113l-402 1060h220z" />
<glyph unicode="z" horiz-adv-x="1030" d="M329 154h625v-154h-860v138l585 788h-578v156h819v-134z" />
<glyph unicode="{" horiz-adv-x="696" d="M631 -364q-195 55 -277 175t-82 290v205q0 112 -51.5 174.5t-157.5 62.5v147q106 0 157.5 61.5t51.5 174.5v206q0 171 82 290.5t277 174.5l40 -117q-110 -35 -156 -125.5t-46 -222.5v-206q0 -105 -42.5 -185t-127.5 -125q85 -46 127.5 -126.5t42.5 -183.5v-205 q0 -132 46 -221.5t156 -125.5z" />
<glyph unicode="|" horiz-adv-x="507" d="M333 -270h-158v1726h158v-1726z" />
<glyph unicode="}" horiz-adv-x="696" d="M21 -246q109 36 156 125.5t47 221.5v205q0 107 45 187t139 123q-94 41 -139 121t-45 189v206q0 132 -47 222.5t-156 125.5l41 117q194 -55 276.5 -174.5t82.5 -290.5v-206q0 -113 50.5 -174.5t158.5 -61.5v-147q-108 0 -158.5 -62.5t-50.5 -174.5v-205q0 -170 -82.5 -290 t-276.5 -175z" />
<glyph unicode="~" horiz-adv-x="1391" d="M1264 740q0 -137 -87 -238t-217 -101q-90 0 -163.5 33t-158.5 107q-59 48 -108 72t-99 24q-67 0 -114.5 -53t-47.5 -128l-141 18q0 136 85.5 232.5t217.5 96.5q88 0 163 -34.5t160 -104.5q58 -51 106 -74t100 -23q66 0 114.5 57t48.5 134z" />
<glyph unicode="&#xa1;" horiz-adv-x="507" d="M341 -374h-197v978h197v-978zM341 876h-197v206h197v-206z" />
<glyph unicode="&#xa2;" horiz-adv-x="1122" d="M593 133q98 0 172.5 60.5t74.5 148.5h178l3 -6q3 -122 -98 -223t-247 -126v-232h-198v236q-182 36 -276.5 182t-94.5 347v42q0 199 95 344.5t276 183.5v228h198v-223q157 -24 252.5 -130.5t92.5 -250.5l-2 -5h-179q0 99 -70 168.5t-177 69.5q-155 0 -221.5 -111.5 t-66.5 -273.5v-42q0 -166 66 -276.5t222 -110.5z" />
<glyph unicode="&#xa3;" horiz-adv-x="1194" d="M430 615l6 -149q0 -90 -15.5 -171.5t-44.5 -140.5h735l-1 -154h-976v154h10q48 13 72 111t24 201l-6 149h-164v155h158l-10 270q0 204 112 320.5t300 116.5q200 0 310 -104.5t106 -276.5l-2 -6h-190q0 118 -63 175t-161 57q-99 0 -157 -74.5t-58 -207.5l10 -270h418v-155 h-413z" />
<glyph unicode="&#xa4;" horiz-adv-x="1456" d="M1097 108q-77 -62 -169.5 -95t-193.5 -33t-193.5 32.5t-167.5 93.5l-130 -132l-139 138l138 140q-50 76 -76.5 166.5t-26.5 189.5q0 102 28.5 196t82.5 172l-146 149l139 139l143 -146q74 55 163 85.5t185 30.5q97 0 186 -31t164 -87l146 149l140 -140l-150 -153 q52 -78 80.5 -170.5t28.5 -193.5q0 -98 -26.5 -187.5t-74.5 -165.5l142 -143l-140 -139zM321 608q0 -188 120.5 -320.5t292.5 -132.5q170 0 290.5 132.5t120.5 320.5q0 186 -120.5 318t-290.5 132q-172 0 -292.5 -132t-120.5 -318z" />
<glyph unicode="&#xa5;" horiz-adv-x="1243" d="M615 793l360 663h224l-418 -718h312v-155h-383v-135h383v-155h-383v-293h-197v293h-375v155h375v135h-375v155h311l-419 718h226z" />
<glyph unicode="&#xa6;" horiz-adv-x="499" d="M145 -270v792h197v-792h-197zM342 698h-197v758h197v-758z" />
<glyph unicode="&#xa7;" horiz-adv-x="1259" d="M1148 431q0 -94 -48 -164t-135 -110q69 -51 104 -119t35 -166q0 -172 -133 -269.5t-358 -97.5q-221 0 -372 102.5t-146 322.5l2 6l188 2q0 -143 96.5 -210.5t231.5 -67.5q137 0 215.5 59.5t78.5 150.5t-72 141.5t-276 113.5q-239 63 -352 156t-113 270q0 91 47 161.5 t134 111.5q-68 50 -102 119.5t-34 166.5q0 166 134 266.5t358 100.5q233 0 363 -111.5t126 -313.5l-3 -6h-188q0 118 -79 197t-219 79q-145 0 -220 -59.5t-75 -150.5q0 -99 67 -148.5t278 -107.5q244 -69 355.5 -159.5t111.5 -265.5zM603 689q-47 12 -88.5 24.5t-77.5 27.5 q-73 -19 -109.5 -69t-36.5 -119q0 -100 68 -151.5t278 -110.5q56 -17 93 -28t70 -23q72 20 112 69.5t40 118.5q0 91 -73.5 144.5t-275.5 116.5z" />
<glyph unicode="&#xa8;" horiz-adv-x="1021" d="M859 1256h-219v200h219v-200zM389 1256h-219v200h219v-200z" />
<glyph unicode="&#xa9;" horiz-adv-x="1604" d="M1111 596l2 -6q4 -151 -84 -229.5t-245 -78.5q-160 0 -254 106.5t-94 280.5v119q0 173 94 280t254 107q157 0 245.5 -79t84.5 -228l-2 -6h-146q0 95 -45.5 138.5t-136.5 43.5q-94 0 -145 -70.5t-51 -184.5v-120q0 -117 51 -187t145 -70q91 0 136 43t45 141h146zM209 729 q0 -265 171.5 -447t417.5 -182q245 0 417 182t172 447q0 263 -172 444t-417 181q-246 0 -417.5 -181t-171.5 -444zM88 729q0 315 207 531t503 216q295 0 502 -216t207 -531q0 -316 -207.5 -533t-501.5 -217q-296 0 -503 217t-207 533z" />
<glyph unicode="&#xaa;" horiz-adv-x="917" d="M613 706q-8 21 -13 45t-8 50q-33 -47 -89.5 -78t-133.5 -31q-119 0 -184 61t-65 167q0 110 84.5 170t245.5 60h139v52q0 63 -30 97t-88 34q-67 0 -103.5 -27t-36.5 -76l-162 13l-1 6q-6 98 78.5 163t224.5 65q134 0 212 -71t78 -205v-314q0 -50 6 -94t20 -87h-174z M411 831q51 0 105.5 30t72.5 65v110h-138q-75 0 -116.5 -33t-41.5 -79q0 -45 29 -69t89 -24z" />
<glyph unicode="&#xab;" horiz-adv-x="966" d="M286 516l255 -398h-148l-295 389v19l295 389h148zM620 516l255 -398h-148l-295 389v19l295 389h148z" />
<glyph unicode="&#xac;" horiz-adv-x="1137" d="M962 375h-198v262h-637v165h835v-427z" />
<glyph unicode="&#xad;" horiz-adv-x="561" d="M525 538h-490v154h490v-154z" />
<glyph unicode="&#xae;" horiz-adv-x="1604" d="M88 729q0 315 207 531t503 216q295 0 502 -216t207 -531q0 -316 -207.5 -533t-501.5 -217q-296 0 -503 217t-207 533zM209 729q0 -266 171.5 -447.5t417.5 -181.5q244 0 416 182t172 447q0 264 -171.5 444.5t-416.5 180.5q-246 0 -417.5 -180.5t-171.5 -444.5zM653 654 v-338h-149v850h280q152 0 238.5 -65.5t86.5 -191.5q0 -62 -33 -109t-96 -78q66 -26 95.5 -79t29.5 -128v-56q0 -41 3.5 -73.5t13.5 -53.5v-16h-153q-9 21 -11 61.5t-2 82.5v54q0 72 -33.5 106t-110.5 34h-159zM653 784h152q65 1 110.5 32.5t45.5 87.5q0 73 -39.5 102.5 t-137.5 29.5h-131v-252z" />
<glyph unicode="&#xaf;" horiz-adv-x="950" d="M844 1310h-721v146h721v-146z" />
<glyph unicode="&#xb0;" horiz-adv-x="763" d="M128 1216q0 106 76 183.5t181 77.5q103 0 177.5 -77.5t74.5 -183.5q0 -108 -74 -182.5t-178 -74.5q-106 0 -181.5 74.5t-75.5 182.5zM259 1216q0 -55 36.5 -91t89.5 -36q52 0 87.5 36t35.5 91t-36 92.5t-87 37.5q-53 0 -89.5 -37.5t-36.5 -92.5z" />
<glyph unicode="&#xb1;" horiz-adv-x="1097" d="M657 856h358v-154h-358v-413h-177v413h-381v154h381v411h177v-411zM971 4h-835v155h835v-155z" />
<glyph unicode="&#xb2;" horiz-adv-x="868" d="M714 667h-592v130l302 262q69 60 91 97.5t22 79.5q0 50 -28.5 81t-86.5 31q-67 0 -103.5 -32t-36.5 -82h-161l-2 6q-6 99 78 169t225 70q135 0 211 -64t76 -180q0 -80 -44.5 -136t-160.5 -161l-153 -135l2 -6h361v-130z" />
<glyph unicode="&#xb3;" horiz-adv-x="876" d="M424 1137q67 0 99.5 28.5t32.5 80.5q0 43 -36.5 72t-105.5 29q-56 0 -90.5 -24t-34.5 -64h-162l-2 6q-6 94 78.5 153.5t210.5 59.5q145 0 229 -59.5t84 -169.5q0 -55 -35.5 -100.5t-97.5 -71.5q70 -23 108 -71t38 -116q0 -111 -90 -173t-236 -62q-127 0 -217.5 58 t-84.5 169l1 6h163q0 -46 37.5 -74.5t100.5 -28.5q72 0 114 29.5t42 77.5q0 62 -36.5 90.5t-109.5 28.5h-132v126h132z" />
<glyph unicode="&#xb4;" horiz-adv-x="654" d="M316 1477h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xb5;" d="M349 1082v-642q2 -178 57.5 -242.5t155.5 -64.5q98 0 158.5 36t92.5 106v807h197v-1082h-177l-9 108q-44 -63 -107.5 -96t-146.5 -33q-72 0 -126.5 16.5t-94.5 51.5v-463h-196v1498h196z" />
<glyph unicode="&#xb6;" horiz-adv-x="1006" d="M639 0v520h-84q-233 0 -362.5 129.5t-129.5 338.5q0 207 129.5 337.5t362.5 130.5h281v-1456h-197z" />
<glyph unicode="&#xb7;" horiz-adv-x="540" d="M359 624h-198v212h198v-212z" />
<glyph unicode="&#xb8;" horiz-adv-x="509" d="M292 0l-12 -52q65 -11 108 -52t43 -121q0 -96 -79 -153t-226 -57l-7 109q72 0 116 24.5t44 73.5q0 48 -36 67t-123 26l32 135h140z" />
<glyph unicode="&#xb9;" horiz-adv-x="557" d="M396 665h-174v655h-127v134l301 23v-812z" />
<glyph unicode="&#xba;" horiz-adv-x="933" d="M120 1142q0 148 94 241.5t251 93.5q158 0 252 -93.5t94 -241.5v-117q0 -149 -93.5 -241.5t-250.5 -92.5q-158 0 -252.5 92.5t-94.5 241.5v117zM293 1025q0 -88 44 -140.5t130 -52.5q83 0 127.5 53t44.5 140v117q0 84 -45 137.5t-129 53.5t-128 -53.5t-44 -137.5v-117z " />
<glyph unicode="&#xbb;" horiz-adv-x="966" d="M258 949l295 -389v-19l-295 -389h-148l255 398l-255 399h148zM604 949l295 -389v-19l-295 -389h-148l255 398l-255 399h148z" />
<glyph unicode="&#xbc;" horiz-adv-x="1595" d="M485 664h-174v655h-127v134l301 23v-812zM448 118l-109 67l711 1138l109 -67zM1380 282h126v-130h-126v-152h-170v152h-417l-8 102l422 547h173v-519zM967 282h243v310l-6 1l-13 -22z" />
<glyph unicode="&#xbd;" horiz-adv-x="1708" d="M461 118l-109 67l711 1138l109 -67zM485 664h-174v655h-127v134l301 23v-812zM1525 0h-592v130l302 262q69 60 91 97.5t22 79.5q0 50 -28.5 81t-86.5 31q-67 0 -103.5 -32t-36.5 -82h-161l-2 6q-6 99 78 169t225 70q135 0 211 -64t76 -180q0 -80 -44.5 -136t-160.5 -161 l-153 -135l2 -6h361v-130z" />
<glyph unicode="&#xbe;" horiz-adv-x="1781" d="M631 118l-109 67l711 1138l109 -67zM1569 282h126v-130h-126v-152h-170v152h-417l-8 102l422 547h173v-519zM1156 282h243v310l-6 1l-13 -22zM440 1138q67 0 99.5 28.5t32.5 80.5q0 43 -36.5 72t-105.5 29q-56 0 -90.5 -24t-34.5 -64h-162l-2 6q-6 94 78.5 153.5 t210.5 59.5q145 0 229 -59.5t84 -169.5q0 -55 -35.5 -100.5t-97.5 -71.5q70 -23 108 -71t38 -116q0 -111 -90 -173t-236 -62q-127 0 -217.5 58t-84.5 169l1 6h163q0 -46 37.5 -74.5t100.5 -28.5q72 0 114 29.5t42 77.5q0 62 -36.5 90.5t-109.5 28.5h-132v126h132z" />
<glyph unicode="&#xbf;" horiz-adv-x="1013" d="M645 672q-2 -146 -26 -202t-125 -147q-100 -118 -141.5 -181t-41.5 -150q0 -106 56 -163t162 -57q90 0 154.5 49.5t64.5 145.5h188l3 -6q2 -161 -114.5 -258t-295.5 -97q-198 0 -306.5 100.5t-108.5 280.5q0 127 70 233.5t187 220.5q53 53 65 96t12 135h197zM441 1083 h206v-209h-206v209z" />
<glyph unicode="&#xc0;" horiz-adv-x="1326" d="M969 375h-610l-138 -375h-201l563 1456h169l554 -1456h-201zM420 540h490l-240 663h-6zM786 1560h-158l-250 260l3 6h230z" />
<glyph unicode="&#xc1;" horiz-adv-x="1326" d="M969 375h-610l-138 -375h-201l563 1456h169l554 -1456h-201zM420 540h490l-240 663h-6zM798 1822h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xc2;" horiz-adv-x="1326" d="M969 375h-610l-138 -375h-201l563 1456h169l554 -1456h-201zM420 540h490l-240 663h-6zM970 1626v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xc3;" horiz-adv-x="1326" d="M969 375h-610l-138 -375h-201l563 1456h169l554 -1456h-201zM420 540h490l-240 663h-6zM1010 1844q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5l-108 26q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5z" />
<glyph unicode="&#xc4;" horiz-adv-x="1326" d="M969 375h-610l-138 -375h-201l563 1456h169l554 -1456h-201zM420 540h490l-240 663h-6zM1008 1605h-219v200h219v-200zM538 1605h-219v200h219v-200z" />
<glyph unicode="&#xc5;" horiz-adv-x="1326" d="M969 375h-610l-138 -375h-201l563 1456h169l554 -1456h-201zM420 540h490l-240 663h-6zM457 1734q0 84 60.5 141t147.5 57q85 0 145 -56.5t60 -141.5q0 -86 -59.5 -140t-145.5 -54q-87 0 -147.5 54t-60.5 140zM560 1734q0 -43 31 -73.5t74 -30.5q42 0 72 29.5t30 74.5 t-30 76t-72 31q-43 0 -74 -31t-31 -76z" />
<glyph unicode="&#xc6;" horiz-adv-x="1922" d="M1889 0h-895l-15 350h-557l-202 -350h-240l880 1456h967v-155h-691l20 -466h590v-155h-584l22 -526h705v-154zM525 529h447l-31 710l-5 2z" />
<glyph unicode="&#xc7;" horiz-adv-x="1297" d="M1209 466l2 -6q4 -205 -144 -343t-391 -138q-247 0 -402.5 175t-155.5 444v259q0 269 155.5 444.5t402.5 175.5t393 -131.5t142 -348.5l-2 -6h-189q0 153 -90 242t-254 89q-165 0 -263 -133t-98 -330v-261q0 -199 98 -332t263 -133q164 0 254 88.5t90 244.5h189zM754 -8 l-12 -52q65 -11 108 -52t43 -121q0 -96 -79 -153t-226 -57l-7 109q72 0 116 24.5t44 73.5q0 48 -36 67t-123 26l32 135h140z" />
<glyph unicode="&#xc8;" horiz-adv-x="1197" d="M1044 675h-667v-521h769v-154h-966v1456h955v-155h-758v-471h667v-155zM711 1560h-158l-250 260l3 6h230z" />
<glyph unicode="&#xc9;" horiz-adv-x="1197" d="M1044 675h-667v-521h769v-154h-966v1456h955v-155h-758v-471h667v-155zM723 1822h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xca;" horiz-adv-x="1197" d="M1044 675h-667v-521h769v-154h-966v1456h955v-155h-758v-471h667v-155zM936 1626v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xcb;" horiz-adv-x="1197" d="M1044 675h-667v-521h769v-154h-966v1456h955v-155h-758v-471h667v-155zM973 1605h-219v200h219v-200zM503 1605h-219v200h219v-200z" />
<glyph unicode="&#xcc;" horiz-adv-x="579" d="M388 0h-198v1456h198v-1456zM374 1560h-158l-250 260l3 6h230z" />
<glyph unicode="&#xcd;" horiz-adv-x="579" d="M388 0h-198v1456h198v-1456zM384 1822h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xce;" horiz-adv-x="579" d="M388 0h-198v1456h198v-1456zM599 1626v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xcf;" horiz-adv-x="579" d="M388 0h-198v1456h198v-1456zM636 1605h-219v200h219v-200zM166 1605h-219v200h219v-200z" />
<glyph unicode="&#xd0;" horiz-adv-x="1379" d="M210 0v663h-168v155h168v638h447q286 0 459 -175.5t173 -453.5v-199q0 -279 -173 -453.5t-459 -174.5h-447zM683 663h-276v-509h250q202 0 318.5 133t116.5 341v201q0 206 -116.5 339t-318.5 133h-250v-483h276v-155z" />
<glyph unicode="&#xd1;" horiz-adv-x="1461" d="M1281 0h-197l-701 1126l-6 -2v-1124h-197v1456h197l701 -1124l6 2v1122h197v-1456zM1075 1844q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5l-108 26q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5z" />
<glyph unicode="&#xd2;" horiz-adv-x="1396" d="M1282 598q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259zM1085 859q0 200 -108 328t-290 128q-172 0 -274.5 -128t-102.5 -328v-261q0 -202 102.5 -330t274.5 -128 q183 0 290.5 127.5t107.5 330.5v261zM781 1581h-158l-250 260l3 6h230z" />
<glyph unicode="&#xd3;" horiz-adv-x="1396" d="M1282 598q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259zM1085 859q0 200 -108 328t-290 128q-172 0 -274.5 -128t-102.5 -328v-261q0 -202 102.5 -330t274.5 -128 q183 0 290.5 127.5t107.5 330.5v261zM793 1843h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xd4;" horiz-adv-x="1396" d="M1282 598q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259zM1085 859q0 200 -108 328t-290 128q-172 0 -274.5 -128t-102.5 -328v-261q0 -202 102.5 -330t274.5 -128 q183 0 290.5 127.5t107.5 330.5v261zM1006 1647v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xd5;" horiz-adv-x="1396" d="M1282 598q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259zM1085 859q0 200 -108 328t-290 128q-172 0 -274.5 -128t-102.5 -328v-261q0 -202 102.5 -330t274.5 -128 q183 0 290.5 127.5t107.5 330.5v261zM1045 1865q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5l-108 26q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5z" />
<glyph unicode="&#xd6;" horiz-adv-x="1396" d="M1282 598q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259zM1085 859q0 200 -108 328t-290 128q-172 0 -274.5 -128t-102.5 -328v-261q0 -202 102.5 -330t274.5 -128 q183 0 290.5 127.5t107.5 330.5v261zM1043 1626h-219v200h219v-200zM573 1626h-219v200h219v-200z" />
<glyph unicode="&#xd7;" horiz-adv-x="1096" d="M88 351l327 334l-327 334l126 126l326 -333l327 333l126 -126l-328 -334l328 -334l-126 -126l-327 332l-326 -332z" />
<glyph unicode="&#xd8;" horiz-adv-x="1396" d="M1282 598q0 -267 -165.5 -443t-429.5 -176q-85 0 -160.5 20.5t-139.5 60.5l-91 -154h-149l139 234q-84 84 -128.5 202t-44.5 256v259q0 266 159.5 443t414.5 177q94 0 178.5 -25.5t156.5 -71.5l81 137h149l-132 -221q77 -84 119.5 -197t42.5 -242v-259zM310 598 q0 -85 19 -158t54 -125l6 -1l544 916q-50 41 -112 63t-134 22q-172 0 -274.5 -128t-102.5 -328v-261zM1085 859q0 75 -16.5 142t-46.5 117l-6 1l-541 -911q44 -34 97 -51t115 -17q183 0 290.5 127.5t107.5 330.5v261z" />
<glyph unicode="&#xd9;" horiz-adv-x="1386" d="M1244 1456v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197zM780 1560h-158l-250 260l3 6h230z" />
<glyph unicode="&#xda;" horiz-adv-x="1386" d="M1244 1456v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197zM792 1822h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xdb;" horiz-adv-x="1386" d="M1244 1456v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197zM1005 1626v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xdc;" horiz-adv-x="1386" d="M1244 1456v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197zM1042 1605h-219v200h219v-200zM572 1605h-219v200h219v-200z" />
<glyph unicode="&#xdd;" horiz-adv-x="1250" d="M625 716l380 740h225l-511 -944v-512h-196v525l-503 931h225zM720 1821h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xde;" horiz-adv-x="1209" d="M360 1456v-293h269q232 0 362 -118t130 -307q0 -190 -130 -307.5t-362 -117.5h-269v-313h-197v1456h197zM360 1009v-542h269q147 0 220.5 78t73.5 191q0 114 -73.5 193.5t-220.5 79.5h-269z" />
<glyph unicode="&#xdf;" horiz-adv-x="1221" d="M333 0h-196v1082q0 223 117.5 348t300.5 125q161 0 262 -86t101 -253q0 -118 -64.5 -228t-64.5 -167q0 -82 173.5 -224t173.5 -281q0 -167 -104.5 -252t-282.5 -85q-84 0 -172.5 20.5t-125.5 50.5l44 159q43 -28 108 -52t126 -24q108 0 159 47.5t51 125.5 q0 84 -173.5 227.5t-173.5 289.5q0 80 70.5 190.5t70.5 186.5q0 93 -51 147t-117 54q-104 0 -168 -83.5t-64 -235.5v-1082z" />
<glyph unicode="&#xe0;" horiz-adv-x="1126" d="M808 0q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5 t112 -269.5v-521q0 -58 6 -112t22 -106h-203zM481 144q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141q0 -72 45 -114t133 -42zM638 1238h-158l-250 260l3 6h230z" />
<glyph unicode="&#xe1;" horiz-adv-x="1126" d="M808 0q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5 t112 -269.5v-521q0 -58 6 -112t22 -106h-203zM481 144q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141q0 -72 45 -114t133 -42zM650 1500h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xe2;" horiz-adv-x="1126" d="M808 0q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5 t112 -269.5v-521q0 -58 6 -112t22 -106h-203zM481 144q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141q0 -72 45 -114t133 -42zM863 1304v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xe3;" horiz-adv-x="1126" d="M808 0q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5 t112 -269.5v-521q0 -58 6 -112t22 -106h-203zM481 144q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141q0 -72 45 -114t133 -42zM902 1522q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5l-108 26q0 93 59 161.5t150 68.5 q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5z" />
<glyph unicode="&#xe4;" horiz-adv-x="1126" d="M808 0q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5 t112 -269.5v-521q0 -58 6 -112t22 -106h-203zM481 144q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141q0 -72 45 -114t133 -42zM900 1283h-219v200h219v-200zM430 1283h-219v200h219v-200z" />
<glyph unicode="&#xe5;" horiz-adv-x="1126" d="M808 0q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5 t112 -269.5v-521q0 -58 6 -112t22 -106h-203zM481 144q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141q0 -72 45 -114t133 -42zM346 1412q0 84 60.5 141t147.5 57q85 0 145 -56.5t60 -141.5q0 -86 -59.5 -140t-145.5 -54q-87 0 -147.5 54t-60.5 140zM449 1412 q0 -43 31 -73.5t74 -30.5q42 0 72 29.5t30 74.5t-30 76t-72 31q-43 0 -74 -31t-31 -76z" />
<glyph unicode="&#xe6;" horiz-adv-x="1729" d="M1262 -21q-136 0 -240 48.5t-170 138.5q-56 -79 -167.5 -133t-271.5 -54q-170 0 -262.5 87t-92.5 238q0 158 115 244.5t335 86.5h229v85q0 106 -51.5 166.5t-149.5 60.5q-103 0 -164 -55t-61 -133l-188 18l-2 6q-5 138 109.5 228.5t305.5 90.5q114 0 201.5 -40.5 t137.5 -117.5q64 75 151.5 116.5t188.5 41.5q214 0 329.5 -130t115.5 -358v-119h-709l-2 -5q1 -159 79.5 -258t233.5 -99q103 0 169.5 27.5t144.5 78.5l67 -138q-53 -44 -147 -83t-234 -39zM452 133q76 0 159 43.5t126 100.5v216h-227q-120 0 -187.5 -56t-67.5 -137 q0 -74 50 -120.5t147 -46.5zM1215 947q-113 0 -182 -84.5t-80 -217.5l2 -5h508v31q0 122 -60 199t-188 77z" />
<glyph unicode="&#xe7;" horiz-adv-x="1087" d="M583 133q98 0 172.5 60.5t74.5 148.5h177l2 -6q5 -140 -124.5 -248.5t-301.5 -108.5q-236 0 -361 154t-125 387v42q0 231 125.5 385.5t360.5 154.5q190 0 310.5 -112t116.5 -275l-2 -6h-178q0 99 -70 168.5t-177 69.5q-155 0 -221.5 -111.5t-66.5 -273.5v-42 q0 -166 66 -276.5t222 -110.5zM613 -8l-12 -52q65 -11 108 -52t43 -121q0 -96 -79 -153t-226 -57l-7 109q72 0 116 24.5t44 73.5q0 48 -36 67t-123 26l32 135h140z" />
<glyph unicode="&#xe8;" horiz-adv-x="1083" d="M592 -21q-226 0 -359.5 150.5t-133.5 390.5v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38zM562 947q-99 0 -169 -83.5t-86 -209.5l2 -5h499 v26q0 116 -62 194t-184 78zM641 1239h-158l-250 260l3 6h230z" />
<glyph unicode="&#xe9;" horiz-adv-x="1083" d="M592 -21q-226 0 -359.5 150.5t-133.5 390.5v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38zM562 947q-99 0 -169 -83.5t-86 -209.5l2 -5h499 v26q0 116 -62 194t-184 78zM653 1501h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xea;" horiz-adv-x="1083" d="M592 -21q-226 0 -359.5 150.5t-133.5 390.5v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38zM562 947q-99 0 -169 -83.5t-86 -209.5l2 -5h499 v26q0 116 -62 194t-184 78zM866 1305v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xeb;" horiz-adv-x="1083" d="M592 -21q-226 0 -359.5 150.5t-133.5 390.5v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38zM562 947q-99 0 -169 -83.5t-86 -209.5l2 -5h499 v26q0 116 -62 194t-184 78zM903 1284h-219v200h219v-200zM433 1284h-219v200h219v-200z" />
<glyph unicode="&#xec;" horiz-adv-x="515" d="M350 0h-197v1082h197v-1082zM337 1217h-158l-250 260l3 6h230z" />
<glyph unicode="&#xed;" horiz-adv-x="515" d="M350 0h-197v1082h197v-1082zM347 1479h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xee;" horiz-adv-x="515" d="M350 0h-197v1082h197v-1082zM562 1283v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xef;" horiz-adv-x="515" d="M350 0h-197v1082h197v-1082zM599 1262h-219v200h219v-200zM129 1262h-219v200h219v-200z" />
<glyph unicode="&#xf0;" horiz-adv-x="1202" d="M873 1297q95 -104 147 -241.5t52 -300.5v-220q0 -245 -144 -400.5t-359 -155.5q-218 0 -357.5 140t-139.5 347q0 228 138 370t351 142q90 0 169.5 -37t131.5 -97l4 5q-9 109 -51.5 197t-110.5 154l-290 -165l-77 102l256 146q-39 22 -80.5 39t-85.5 31l60 164 q79 -19 151 -52t135 -79l218 125l77 -102zM573 133q133 0 217.5 114t84.5 288v148q-35 59 -115.5 99.5t-198.5 40.5q-131 0 -211.5 -104t-80.5 -253q0 -132 82 -232.5t222 -100.5z" />
<glyph unicode="&#xf1;" d="M319 1082l14 -161q54 86 135.5 133.5t185.5 47.5q175 0 271 -102.5t96 -316.5v-683h-197v679q0 143 -56.5 203t-172.5 60q-85 0 -150.5 -41t-104.5 -112v-789h-197v1082h176zM925 1522q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5 l-108 26q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5z" />
<glyph unicode="&#xf2;" d="M97 551q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5v22zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z M665 1238h-158l-250 260l3 6h230z" />
<glyph unicode="&#xf3;" d="M97 551q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5v22zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z M677 1500h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xf4;" d="M97 551q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5v22zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z M890 1304v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xf5;" d="M97 551q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5v22zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z M929 1522q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5l-108 26q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5z" />
<glyph unicode="&#xf6;" d="M97 551q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5v22zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z M927 1283h-219v200h219v-200zM457 1283h-219v200h219v-200z" />
<glyph unicode="&#xf7;" horiz-adv-x="1170" d="M1069 597h-998v188h998v-188zM670 999h-198v203h198v-203zM670 180h-198v203h198v-203z" />
<glyph unicode="&#xf8;" d="M97 551q0 240 130 395.5t353 155.5q56 0 107.5 -11t97.5 -31l74 149h129l-104 -211q88 -74 135 -190t47 -257v-22q0 -242 -130 -396t-354 -154q-51 0 -97 8.5t-88 24.5l-72 -147h-129l100 204q-96 71 -147.5 191t-51.5 269v22zM294 529q0 -91 20 -166.5t61 -123.5h6 l332 674q-29 16 -62.5 25t-70.5 9q-141 0 -213.5 -113t-72.5 -283v-22zM869 551q0 80 -17.5 150.5t-49.5 117.5h-6l-327 -663q24 -12 52 -17.5t61 -5.5q141 0 214 112t73 284v22z" />
<glyph unicode="&#xf9;" d="M830 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177zM663 1217h-158l-250 260l3 6h230z" />
<glyph unicode="&#xfa;" d="M830 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177zM675 1479h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xfb;" d="M830 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177zM888 1283v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#xfc;" d="M830 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177zM925 1262h-219v200h219v-200zM455 1262h-219v200h219v-200z" />
<glyph unicode="&#xfd;" horiz-adv-x="1030" d="M474 401l35 -136h6l266 817h219l-455 -1248q-41 -109 -117.5 -190t-206.5 -81q-24 0 -61 5.5t-57 10.5l20 155q-6 1 35.5 -2t52.5 -3q63 0 103 56t67 124l47 113l-402 1060h220zM609 1479h230l2 -6l-270 -260h-147z" />
<glyph unicode="&#xfe;" horiz-adv-x="1186" d="M1075 500q0 -234 -112 -377.5t-309 -143.5q-100 0 -175.5 33.5t-128.5 100.5v-529h-197v1976h197v-598q53 68 128 104t173 36q201 0 312.5 -159.5t111.5 -421.5v-21zM878 521q0 184 -72.5 302.5t-213.5 118.5q-85 0 -145 -38.5t-97 -105.5v-523q37 -67 97 -104.5 t147 -37.5q140 0 212 102.5t72 264.5v21z" />
<glyph unicode="&#xff;" horiz-adv-x="1030" d="M474 401l35 -136h6l266 817h219l-455 -1248q-41 -109 -117.5 -190t-206.5 -81q-24 0 -61 5.5t-57 10.5l20 155q-6 1 35.5 -2t52.5 -3q63 0 103 56t67 124l47 113l-402 1060h220zM859 1262h-219v200h219v-200zM389 1262h-219v200h219v-200z" />
<glyph unicode="&#x131;" horiz-adv-x="515" d="M350 0h-197v1082h197v-1082z" />
<glyph unicode="&#x152;" horiz-adv-x="1960" d="M1871 0h-918q-92 -10 -157 -15.5t-132 -5.5q-249 0 -404.5 166t-155.5 431v304q0 265 154.5 431t403.5 166q69 0 140.5 -6t150.5 -15h907v-155h-758v-471h667v-155h-667v-521h769v-154zM664 134q61 0 122 4.5t119 13.5v1151q-61 8 -122 13.5t-121 5.5q-169 0 -265 -113.5 t-96 -326.5v-306q0 -214 97 -328t266 -114z" />
<glyph unicode="&#x153;" horiz-adv-x="1854" d="M1370 -21q-131 0 -232.5 52.5t-166.5 148.5q-64 -96 -163 -148.5t-226 -52.5q-224 0 -354.5 154.5t-130.5 395.5v22q0 240 130 395.5t353 155.5q130 0 230 -54.5t164 -152.5q65 97 161.5 152t204.5 55q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5 t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22zM1340 947q-99 0 -169 -83.5t-86 -209.5l2 -5h499v26 q0 116 -62 194t-184 78z" />
<glyph unicode="&#x178;" horiz-adv-x="1250" d="M625 716l380 740h225l-511 -944v-512h-196v525l-503 931h225zM970 1604h-219v200h219v-200zM500 1604h-219v200h219v-200z" />
<glyph unicode="&#x2c6;" horiz-adv-x="979" d="M785 1277v-25h-161l-147 148l-146 -148h-160v26l246 237h120z" />
<glyph unicode="&#x2da;" horiz-adv-x="696" d="M133 1308q0 84 60.5 141t147.5 57q85 0 145 -56.5t60 -141.5q0 -86 -59.5 -140t-145.5 -54q-87 0 -147.5 54t-60.5 140zM236 1308q0 -43 31 -73.5t74 -30.5q42 0 72 29.5t30 74.5t-30 76t-72 31q-43 0 -74 -31t-31 -76z" />
<glyph unicode="&#x2dc;" horiz-adv-x="979" d="M829 1491q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5l-108 26q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5z" />
<glyph unicode="&#x2000;" horiz-adv-x="966" />
<glyph unicode="&#x2001;" horiz-adv-x="1932" />
<glyph unicode="&#x2002;" horiz-adv-x="966" />
<glyph unicode="&#x2003;" horiz-adv-x="1932" />
<glyph unicode="&#x2004;" horiz-adv-x="644" />
<glyph unicode="&#x2005;" horiz-adv-x="483" />
<glyph unicode="&#x2006;" horiz-adv-x="322" />
<glyph unicode="&#x2007;" horiz-adv-x="322" />
<glyph unicode="&#x2008;" horiz-adv-x="241" />
<glyph unicode="&#x2009;" horiz-adv-x="386" />
<glyph unicode="&#x200a;" horiz-adv-x="107" />
<glyph unicode="&#x2010;" horiz-adv-x="561" d="M525 538h-490v154h490v-154z" />
<glyph unicode="&#x2011;" horiz-adv-x="561" d="M525 538h-490v154h490v-154z" />
<glyph unicode="&#x2012;" horiz-adv-x="561" d="M525 538h-490v154h490v-154z" />
<glyph unicode="&#x2013;" horiz-adv-x="1416" d="M1265.89 648h-1085.49v155h1085.49v-155z" />
<glyph unicode="&#x2014;" horiz-adv-x="1660" d="M1524.78 648h-1335.98v155h1335.98v-155z" />
<glyph unicode="&#x2018;" horiz-adv-x="418" d="M80 1204l160 356h97l-60 -362v-178h-197v184z" />
<glyph unicode="&#x2019;" horiz-adv-x="418" d="M337 1367l-160 -346h-97l60 343v196h197v-193z" />
<glyph unicode="&#x201a;" horiz-adv-x="417" d="M337 26l-160 -281h-97l60 263v241h197v-223z" />
<glyph unicode="&#x201c;" horiz-adv-x="744" d="M80 1204l160 356h97l-60 -362v-178h-197v184zM409 1204l160 356h97l-60 -362v-178h-197v184z" />
<glyph unicode="&#x201d;" horiz-adv-x="752" d="M337 1367l-160 -346h-97l60 343v196h197v-193zM674 1367l-160 -346h-97l60 343v196h197v-193z" />
<glyph unicode="&#x201e;" horiz-adv-x="726" d="M337 96l-160 -335h-97l60 325v194h197v-184zM645 96l-160 -335h-97l60 333v186h197v-184z" />
<glyph unicode="&#x2022;" horiz-adv-x="695" d="M137 793q0 88 56 144t150 56q95 0 151.5 -56t56.5 -144v-60q0 -89 -56 -143.5t-151 -54.5t-151 55t-56 143v60z" />
<glyph unicode="&#x2026;" horiz-adv-x="1380" d="M358 0h-197v202h197v-202zM801 0h-197v202h197v-202zM1221 0h-197v202h197v-202z" />
<glyph unicode="&#x202f;" horiz-adv-x="386" />
<glyph unicode="&#x2039;" horiz-adv-x="615" d="M296 550l255 -398h-148l-295 389v19l295 389h148z" />
<glyph unicode="&#x203a;" horiz-adv-x="615" d="M236 949l295 -389v-19l-295 -389h-148l255 398l-255 399h148z" />
<glyph unicode="&#x2044;" horiz-adv-x="933" d="M168 110l-109 67l711 1138l109 -67z" />
<glyph unicode="&#x205f;" horiz-adv-x="483" />
<glyph unicode="&#x20ac;" horiz-adv-x="1088" d="M914 481h-485l-2 -5q-4 -138 81.5 -240.5t232.5 -102.5q57 0 113 8.5t108 25.5l19 -157q-56 -15 -117.5 -23t-122.5 -8q-231 0 -373.5 144.5t-142.5 357.5h-146v155h146v136h-146v155h146v15q0 244 141.5 389.5t372.5 145.5q59 0 117.5 -8t124.5 -23l-19 -159 q-54 16 -110.5 25.5t-112.5 9.5q-146 0 -231.5 -103t-85.5 -275v-17h492v-155h-492v-136h492v-155z" />
<glyph unicode="&#x2122;" horiz-adv-x="1284" d="M1027 1262l-6 2l-150 -345h-51l-156 359l-6 -2v-357h-93v537h116l161 -390h6l162 390h110v-537h-93v343zM487 1374h-145v-455h-94v455h-145v82h384v-82z" />
<glyph unicode="&#x2212;" horiz-adv-x="1170" d="M1003 648h-835v155h835v-155z" />
<glyph unicode="&#x25fc;" horiz-adv-x="1080" d="M0 1080h1080v-1080h-1080v1080z" />
<glyph unicode="&#xfb02;" horiz-adv-x="1223" d="M225 0v936h-169v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197zM1063 0h-197v1560h197v-1560z" />
<glyph unicode="&#xfb03;" horiz-adv-x="1847" d="M225 0v936h-169v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197zM905 0v936h-170v146h170v117q0 182 106.5 282t295.5 100q67 0 132 -15.5t153 -45.5 l-34 -160q-53 21 -113 36t-123 15q-117 0 -168.5 -52t-51.5 -160v-117h215v-146h-215v-936h-197zM1688 0h-198v1082h198v-1082z" />
<glyph unicode="&#xfb04;" horiz-adv-x="1930" d="M225 0v936h-169v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197zM932 0v936h-169v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5 l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197zM1770 0h-197v1560h197v-1560z" />
<glyph horiz-adv-x="916" d="M682 282h126v-130h-126v-152h-170v152h-417l-8 102l422 547h173v-519zM269 282h243v310l-6 1l-13 -22z" />
<glyph horiz-adv-x="876" d="M424 471q67 0 99.5 28.5t32.5 80.5q0 43 -36.5 72t-105.5 29q-56 0 -90.5 -24t-34.5 -64h-162l-2 6q-6 94 78.5 153.5t210.5 59.5q145 0 229 -59.5t84 -169.5q0 -55 -35.5 -100.5t-97.5 -71.5q70 -23 108 -71t38 -116q0 -111 -90 -173t-236 -62q-127 0 -217.5 58 t-84.5 169l1 6h163q0 -46 37.5 -74.5t100.5 -28.5q72 0 114 29.5t42 77.5q0 62 -36.5 90.5t-109.5 28.5h-132v126h132z" />
<glyph horiz-adv-x="859" d="M714 0h-592v130l302 262q69 60 91 97.5t22 79.5q0 50 -28.5 81t-86.5 31q-67 0 -103.5 -32t-36.5 -82h-161l-2 6q-6 99 78 169t225 70q135 0 211 -64t76 -180q0 -80 -44.5 -136t-160.5 -161l-153 -135l2 -6h361v-130z" />
<glyph horiz-adv-x="557" d="M396 0h-174v655h-127v134l301 23v-812z" />
<hkern u1="&#x22;" u2="w" k="-11" />
<hkern u1="&#x27;" u2="w" k="-11" />
<hkern u1="&#x28;" u2="&#x178;" k="-22" />
<hkern u1="&#x28;" u2="&#xdd;" k="-22" />
<hkern u1="&#x28;" u2="Y" k="-22" />
<hkern u1="&#x28;" u2="W" k="-37" />
<hkern u1="&#x28;" u2="V" k="-20" />
<hkern u1="A" u2="w" k="33" />
<hkern u1="A" u2="t" k="17" />
<hkern u1="A" u2="&#x3f;" k="80" />
<hkern u1="C" u2="&#x7d;" k="17" />
<hkern u1="C" u2="]" k="12" />
<hkern u1="C" u2="&#x29;" k="26" />
<hkern u1="D" u2="&#xc6;" k="33" />
<hkern u1="E" u2="w" k="22" />
<hkern u1="E" u2="f" k="18" />
<hkern u1="F" u2="&#x2026;" k="273" />
<hkern u1="F" u2="&#x201e;" k="273" />
<hkern u1="F" u2="&#x201a;" k="273" />
<hkern u1="F" u2="&#x153;" k="21" />
<hkern u1="F" u2="&#xff;" k="24" />
<hkern u1="F" u2="&#xfd;" k="24" />
<hkern u1="F" u2="&#xfc;" k="22" />
<hkern u1="F" u2="&#xfb;" k="22" />
<hkern u1="F" u2="&#xfa;" k="22" />
<hkern u1="F" u2="&#xf9;" k="22" />
<hkern u1="F" u2="&#xf6;" k="21" />
<hkern u1="F" u2="&#xf5;" k="21" />
<hkern u1="F" u2="&#xf4;" k="21" />
<hkern u1="F" u2="&#xf3;" k="21" />
<hkern u1="F" u2="&#xf2;" k="21" />
<hkern u1="F" u2="&#xeb;" k="21" />
<hkern u1="F" u2="&#xea;" k="21" />
<hkern u1="F" u2="&#xe9;" k="21" />
<hkern u1="F" u2="&#xe8;" k="21" />
<hkern u1="F" u2="&#xe7;" k="21" />
<hkern u1="F" u2="&#xe5;" k="34" />
<hkern u1="F" u2="&#xe4;" k="34" />
<hkern u1="F" u2="&#xe3;" k="34" />
<hkern u1="F" u2="&#xe2;" k="34" />
<hkern u1="F" u2="&#xe1;" k="34" />
<hkern u1="F" u2="&#xe0;" k="34" />
<hkern u1="F" u2="&#xc5;" k="59" />
<hkern u1="F" u2="&#xc4;" k="59" />
<hkern u1="F" u2="&#xc3;" k="59" />
<hkern u1="F" u2="&#xc2;" k="59" />
<hkern u1="F" u2="&#xc1;" k="59" />
<hkern u1="F" u2="&#xc0;" k="59" />
<hkern u1="F" u2="y" k="24" />
<hkern u1="F" u2="v" k="24" />
<hkern u1="F" u2="u" k="22" />
<hkern u1="F" u2="r" k="26" />
<hkern u1="F" u2="q" k="21" />
<hkern u1="F" u2="o" k="21" />
<hkern u1="F" u2="g" k="21" />
<hkern u1="F" u2="e" k="21" />
<hkern u1="F" u2="d" k="21" />
<hkern u1="F" u2="c" k="21" />
<hkern u1="F" u2="a" k="34" />
<hkern u1="F" u2="T" k="-20" />
<hkern u1="F" u2="A" k="59" />
<hkern u1="F" u2="&#x3a;" k="273" />
<hkern u1="F" u2="&#x2e;" k="273" />
<hkern u1="F" u2="&#x2c;" k="273" />
<hkern u1="K" u2="w" k="63" />
<hkern u1="L" u2="w" k="52" />
<hkern u1="O" u2="&#xc6;" k="33" />
<hkern u1="P" u2="&#xc6;" k="293" />
<hkern u1="P" u2="t" k="-14" />
<hkern u1="Q" u2="&#x178;" k="35" />
<hkern u1="Q" u2="&#xdd;" k="35" />
<hkern u1="Q" u2="Y" k="35" />
<hkern u1="Q" u2="W" k="20" />
<hkern u1="Q" u2="V" k="28" />
<hkern u1="Q" u2="T" k="33" />
<hkern u1="R" u2="&#x178;" k="48" />
<hkern u1="R" u2="&#xdd;" k="48" />
<hkern u1="R" u2="Y" k="48" />
<hkern u1="R" u2="V" k="19" />
<hkern u1="R" u2="T" k="50" />
<hkern u1="T" u2="&#xf8;" k="95" />
<hkern u1="T" u2="&#xe6;" k="84" />
<hkern u1="T" u2="&#xc6;" k="188" />
<hkern u1="T" u2="&#xbb;" k="147" />
<hkern u1="T" u2="&#xab;" k="151" />
<hkern u1="T" u2="w" k="47" />
<hkern u1="T" u2="r" k="65" />
<hkern u1="V" u2="&#x7d;" k="-19" />
<hkern u1="V" u2="r" k="30" />
<hkern u1="V" u2="]" k="-17" />
<hkern u1="V" u2="&#x29;" k="-20" />
<hkern u1="W" u2="&#x7d;" k="-14" />
<hkern u1="W" u2="r" k="21" />
<hkern u1="W" u2="]" k="-12" />
<hkern u1="W" u2="&#x29;" k="-15" />
<hkern u1="Y" u2="&#x2022;" k="45" />
<hkern u1="Y" u2="&#xf8;" k="64" />
<hkern u1="Y" u2="&#xe6;" k="63" />
<hkern u1="Y" u2="&#xc6;" k="96" />
<hkern u1="Y" u2="&#xbb;" k="51" />
<hkern u1="Y" u2="&#xab;" k="82" />
<hkern u1="Y" u2="&#x7d;" k="-19" />
<hkern u1="Y" u2="t" k="22" />
<hkern u1="Y" u2="r" k="40" />
<hkern u1="Y" u2="f" k="22" />
<hkern u1="Y" u2="]" k="-18" />
<hkern u1="Y" u2="&#x2a;" k="49" />
<hkern u1="Y" u2="&#x29;" k="-20" />
<hkern u1="Y" u2="&#x26;" k="30" />
<hkern u1="Z" u2="w" k="27" />
<hkern u1="[" u2="&#xdc;" k="18" />
<hkern u1="[" u2="&#xdb;" k="18" />
<hkern u1="[" u2="&#xda;" k="18" />
<hkern u1="[" u2="&#xd9;" k="18" />
<hkern u1="[" u2="U" k="18" />
<hkern u1="[" u2="J" k="18" />
<hkern u1="f" u2="&#x201d;" k="-16" />
<hkern u1="f" u2="&#x201c;" k="-16" />
<hkern u1="f" u2="&#x2019;" k="-16" />
<hkern u1="f" u2="&#x2018;" k="-16" />
<hkern u1="f" u2="&#x153;" k="24" />
<hkern u1="f" u2="&#xeb;" k="24" />
<hkern u1="f" u2="&#xea;" k="24" />
<hkern u1="f" u2="&#xe9;" k="24" />
<hkern u1="f" u2="&#xe8;" k="24" />
<hkern u1="f" u2="&#xe7;" k="24" />
<hkern u1="f" u2="&#x7d;" k="-19" />
<hkern u1="f" u2="q" k="24" />
<hkern u1="f" u2="g" k="24" />
<hkern u1="f" u2="e" k="24" />
<hkern u1="f" u2="d" k="24" />
<hkern u1="f" u2="c" k="24" />
<hkern u1="f" u2="]" k="-18" />
<hkern u1="f" u2="&#x29;" k="-20" />
<hkern u1="f" u2="&#x27;" k="-16" />
<hkern u1="f" u2="&#x22;" k="-16" />
<hkern u1="k" u2="&#x153;" k="20" />
<hkern u1="k" u2="&#xeb;" k="20" />
<hkern u1="k" u2="&#xea;" k="20" />
<hkern u1="k" u2="&#xe9;" k="20" />
<hkern u1="k" u2="&#xe8;" k="20" />
<hkern u1="k" u2="&#xe7;" k="20" />
<hkern u1="k" u2="q" k="20" />
<hkern u1="k" u2="g" k="20" />
<hkern u1="k" u2="e" k="20" />
<hkern u1="k" u2="d" k="20" />
<hkern u1="k" u2="c" k="20" />
<hkern u1="r" u2="w" k="-17" />
<hkern u1="r" u2="t" k="-17" />
<hkern u1="r" u2="f" k="-15" />
<hkern u1="v" u2="f" k="-13" />
<hkern u1="w" u2="&#x2026;" k="124" />
<hkern u1="w" u2="&#x201e;" k="124" />
<hkern u1="w" u2="&#x201a;" k="124" />
<hkern u1="w" u2="&#x3a;" k="124" />
<hkern u1="w" u2="&#x2e;" k="124" />
<hkern u1="w" u2="&#x2c;" k="124" />
<hkern u1="y" u2="f" k="-13" />
<hkern u1="&#x7b;" u2="&#xdc;" k="20" />
<hkern u1="&#x7b;" u2="&#xdb;" k="20" />
<hkern u1="&#x7b;" u2="&#xda;" k="20" />
<hkern u1="&#x7b;" u2="&#xd9;" k="20" />
<hkern u1="&#x7b;" u2="U" k="20" />
<hkern u1="&#x7b;" u2="J" k="20" />
<hkern u1="&#xc0;" u2="w" k="33" />
<hkern u1="&#xc0;" u2="t" k="17" />
<hkern u1="&#xc0;" u2="&#x3f;" k="80" />
<hkern u1="&#xc1;" u2="w" k="33" />
<hkern u1="&#xc1;" u2="t" k="17" />
<hkern u1="&#xc1;" u2="&#x3f;" k="80" />
<hkern u1="&#xc2;" u2="w" k="33" />
<hkern u1="&#xc2;" u2="t" k="17" />
<hkern u1="&#xc2;" u2="&#x3f;" k="80" />
<hkern u1="&#xc3;" u2="w" k="33" />
<hkern u1="&#xc3;" u2="t" k="17" />
<hkern u1="&#xc3;" u2="&#x3f;" k="80" />
<hkern u1="&#xc4;" u2="w" k="33" />
<hkern u1="&#xc4;" u2="t" k="17" />
<hkern u1="&#xc4;" u2="&#x3f;" k="80" />
<hkern u1="&#xc5;" u2="w" k="33" />
<hkern u1="&#xc5;" u2="t" k="17" />
<hkern u1="&#xc5;" u2="&#x3f;" k="80" />
<hkern u1="&#xc7;" u2="&#x7d;" k="17" />
<hkern u1="&#xc7;" u2="]" k="12" />
<hkern u1="&#xc7;" u2="&#x29;" k="26" />
<hkern u1="&#xc8;" u2="w" k="22" />
<hkern u1="&#xc8;" u2="f" k="18" />
<hkern u1="&#xc9;" u2="w" k="22" />
<hkern u1="&#xc9;" u2="f" k="18" />
<hkern u1="&#xca;" u2="w" k="22" />
<hkern u1="&#xca;" u2="f" k="18" />
<hkern u1="&#xcb;" u2="w" k="22" />
<hkern u1="&#xcb;" u2="f" k="18" />
<hkern u1="&#xd0;" u2="&#xc6;" k="33" />
<hkern u1="&#xd2;" u2="&#xc6;" k="33" />
<hkern u1="&#xd3;" u2="&#xc6;" k="33" />
<hkern u1="&#xd4;" u2="&#xc6;" k="33" />
<hkern u1="&#xd5;" u2="&#xc6;" k="33" />
<hkern u1="&#xd6;" u2="&#xc6;" k="33" />
<hkern u1="&#xdd;" u2="&#x2022;" k="45" />
<hkern u1="&#xdd;" u2="&#xf8;" k="64" />
<hkern u1="&#xdd;" u2="&#xe6;" k="63" />
<hkern u1="&#xdd;" u2="&#xc6;" k="96" />
<hkern u1="&#xdd;" u2="&#xbb;" k="51" />
<hkern u1="&#xdd;" u2="&#xab;" k="82" />
<hkern u1="&#xdd;" u2="&#x7d;" k="-19" />
<hkern u1="&#xdd;" u2="t" k="22" />
<hkern u1="&#xdd;" u2="r" k="40" />
<hkern u1="&#xdd;" u2="f" k="22" />
<hkern u1="&#xdd;" u2="]" k="-18" />
<hkern u1="&#xdd;" u2="&#x2a;" k="49" />
<hkern u1="&#xdd;" u2="&#x29;" k="-20" />
<hkern u1="&#xdd;" u2="&#x26;" k="30" />
<hkern u1="&#xfd;" u2="f" k="-13" />
<hkern u1="&#xff;" u2="f" k="-13" />
<hkern u1="&#x178;" u2="&#x2022;" k="45" />
<hkern u1="&#x178;" u2="&#xf8;" k="64" />
<hkern u1="&#x178;" u2="&#xe6;" k="63" />
<hkern u1="&#x178;" u2="&#xc6;" k="96" />
<hkern u1="&#x178;" u2="&#xbb;" k="51" />
<hkern u1="&#x178;" u2="&#xab;" k="82" />
<hkern u1="&#x178;" u2="&#x7d;" k="-19" />
<hkern u1="&#x178;" u2="t" k="22" />
<hkern u1="&#x178;" u2="r" k="40" />
<hkern u1="&#x178;" u2="f" k="22" />
<hkern u1="&#x178;" u2="]" k="-18" />
<hkern u1="&#x178;" u2="&#x2a;" k="49" />
<hkern u1="&#x178;" u2="&#x29;" k="-20" />
<hkern u1="&#x178;" u2="&#x26;" k="30" />
<hkern u1="&#x2018;" u2="w" k="-11" />
<hkern u1="&#x2019;" u2="w" k="-11" />
<hkern u1="&#x201c;" u2="w" k="-11" />
<hkern u1="&#x201d;" u2="w" k="-11" />
<hkern g1="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="38" />
<hkern g1="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="120" />
<hkern g1="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	k="50" />
<hkern g1="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="59" />
<hkern g1="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	g2="m,n,p,ntilde" 	k="20" />
<hkern g1="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="90" />
<hkern g1="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	g2="s" 	k="91" />
<hkern g1="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="282" />
<hkern g1="B" 	g2="T" 	k="27" />
<hkern g1="B" 	g2="V" 	k="24" />
<hkern g1="B" 	g2="Y,Yacute,Ydieresis" 	k="55" />
<hkern g1="C,Ccedilla" 	g2="T" 	k="29" />
<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="21" />
<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" 	g2="T" 	k="27" />
<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" 	g2="V" 	k="22" />
<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" 	g2="Y,Yacute,Ydieresis" 	k="43" />
<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="121" />
<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" 	g2="X" 	k="22" />
<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" 	g2="Z" 	k="23" />
<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="19" />
<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="19" />
<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" 	g2="T" 	k="-20" />
<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="17" />
<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" 	g2="v,y,yacute,ydieresis" 	k="26" />
<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="-18" />
<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" 	g2="T" 	k="29" />
<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" 	g2="Y,Yacute,Ydieresis" 	k="28" />
<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" 	g2="X" 	k="-17" />
<hkern g1="J,U,Ugrave,Uacute,Ucircumflex,Udieresis" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="22" />
<hkern g1="K" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="26" />
<hkern g1="K" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="27" />
<hkern g1="K" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="23" />
<hkern g1="K" 	g2="v,y,yacute,ydieresis" 	k="40" />
<hkern g1="K" 	g2="hyphen,uni00AD,endash,emdash" 	k="162" />
<hkern g1="K" 	g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" 	k="31" />
<hkern g1="L" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="255" />
<hkern g1="L" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="-19" />
<hkern g1="L" 	g2="T" 	k="206" />
<hkern g1="L" 	g2="V" 	k="205" />
<hkern g1="L" 	g2="Y,Yacute,Ydieresis" 	k="278" />
<hkern g1="L" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="14" />
<hkern g1="L" 	g2="v,y,yacute,ydieresis" 	k="123" />
<hkern g1="L" 	g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" 	k="25" />
<hkern g1="L" 	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis" 	k="24" />
<hkern g1="L" 	g2="W" 	k="93" />
<hkern g1="P" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="177" />
<hkern g1="P" 	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	k="11" />
<hkern g1="P" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="13" />
<hkern g1="P" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="13" />
<hkern g1="P" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="402" />
<hkern g1="P" 	g2="X" 	k="50" />
<hkern g1="P" 	g2="Z" 	k="35" />
<hkern g1="P" 	g2="v,y,yacute,ydieresis" 	k="-15" />
<hkern g1="T" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="108" />
<hkern g1="T" 	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	k="83" />
<hkern g1="T" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="89" />
<hkern g1="T" 	g2="m,n,p,ntilde" 	k="89" />
<hkern g1="T" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="79" />
<hkern g1="T" 	g2="s" 	k="76" />
<hkern g1="T" 	g2="T" 	k="-16" />
<hkern g1="T" 	g2="V" 	k="-16" />
<hkern g1="T" 	g2="Y,Yacute,Ydieresis" 	k="-16" />
<hkern g1="T" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="257" />
<hkern g1="T" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="65" />
<hkern g1="T" 	g2="v,y,yacute,ydieresis" 	k="81" />
<hkern g1="T" 	g2="hyphen,uni00AD,endash,emdash" 	k="271" />
<hkern g1="T" 	g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" 	k="28" />
<hkern g1="T" 	g2="W" 	k="-15" />
<hkern g1="T" 	g2="S" 	k="16" />
<hkern g1="T" 	g2="x" 	k="77" />
<hkern g1="T" 	g2="z" 	k="60" />
<hkern g1="V" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="75" />
<hkern g1="V" 	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	k="46" />
<hkern g1="V" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="44" />
<hkern g1="V" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="46" />
<hkern g1="V" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="215" />
<hkern g1="V" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="28" />
<hkern g1="V" 	g2="v,y,yacute,ydieresis" 	k="11" />
<hkern g1="V" 	g2="hyphen,uni00AD,endash,emdash" 	k="154" />
<hkern g1="V" 	g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" 	k="13" />
<hkern g1="W" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="43" />
<hkern g1="W" 	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	k="33" />
<hkern g1="W" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="31" />
<hkern g1="W" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="31" />
<hkern g1="W" 	g2="T" 	k="-14" />
<hkern g1="W" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="142" />
<hkern g1="W" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="19" />
<hkern g1="W" 	g2="hyphen,uni00AD,endash,emdash" 	k="60" />
<hkern g1="X" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="26" />
<hkern g1="X" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="21" />
<hkern g1="X" 	g2="V" 	k="-14" />
<hkern g1="X" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="21" />
<hkern g1="X" 	g2="v,y,yacute,ydieresis" 	k="31" />
<hkern g1="X" 	g2="hyphen,uni00AD,endash,emdash" 	k="153" />
<hkern g1="X" 	g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" 	k="25" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="148" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	k="63" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="65" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="m,n,p,ntilde" 	k="40" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="65" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="s" 	k="58" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="T" 	k="-17" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="V" 	k="-18" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="Y,Yacute,Ydieresis" 	k="-18" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="230" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="X" 	k="-13" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="39" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="v,y,yacute,ydieresis" 	k="20" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="hyphen,uni00AD,endash,emdash" 	k="150" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" 	k="29" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis" 	k="96" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="W" 	k="-17" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="S" 	k="16" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="x" 	k="23" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="z" 	k="30" />
<hkern g1="Y,Yacute,Ydieresis" 	g2="J" 	k="96" />
<hkern g1="Z" 	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" 	k="-13" />
<hkern g1="Z" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="21" />
<hkern g1="Z" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="21" />
<hkern g1="Z" 	g2="u,ugrave,uacute,ucircumflex,udieresis" 	k="19" />
<hkern g1="Z" 	g2="v,y,yacute,ydieresis" 	k="27" />
<hkern g1="Z" 	g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" 	k="26" />
<hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="17" />
<hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	g2="v,y,yacute,ydieresis" 	k="15" />
<hkern g1="b,p,thorn" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="29" />
<hkern g1="b,p,thorn" 	g2="v,y,yacute,ydieresis" 	k="11" />
<hkern g1="b,p,thorn" 	g2="x" 	k="15" />
<hkern g1="b,p,thorn" 	g2="z" 	k="15" />
<hkern g1="c,ccedilla" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="11" />
<hkern g1="e,egrave,eacute,ecircumflex,edieresis" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="14" />
<hkern g1="e,egrave,eacute,ecircumflex,edieresis" 	g2="v,y,yacute,ydieresis" 	k="13" />
<hkern g1="h,m,n,ntilde" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="16" />
<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="20" />
<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" 	g2="v,y,yacute,ydieresis" 	k="15" />
<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" 	g2="x" 	k="21" />
<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" 	g2="z" 	k="16" />
<hkern g1="r" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="-16" />
<hkern g1="r" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="19" />
<hkern g1="r" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="20" />
<hkern g1="r" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="172" />
<hkern g1="r" 	g2="v,y,yacute,ydieresis" 	k="-18" />
<hkern g1="v,y,yacute,ydieresis" 	g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" 	k="-15" />
<hkern g1="v,y,yacute,ydieresis" 	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" 	k="15" />
<hkern g1="v,y,yacute,ydieresis" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="13" />
<hkern g1="v,y,yacute,ydieresis" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="15" />
<hkern g1="v,y,yacute,ydieresis" 	g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" 	k="165" />
<hkern g1="x" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="20" />
<hkern g1="x" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="39" />
<hkern g1="z" 	g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" 	k="16" />
<hkern g1="z" 	g2="o,ograve,oacute,ocircumflex,otilde,odieresis" 	k="16" />
</font>
</defs></svg> 
\ No newline at end of file
diff --git a/docs/fonts/Roboto/Roboto-Regular-webfont.ttf b/docs/fonts/Roboto/Roboto-Regular-webfont.ttf
new file mode 100755
index 0000000..30be1e4
Binary files /dev/null and b/docs/fonts/Roboto/Roboto-Regular-webfont.ttf differ
diff --git a/docs/fonts/Roboto/Roboto-Regular-webfont.woff b/docs/fonts/Roboto/Roboto-Regular-webfont.woff
new file mode 100755
index 0000000..8aa07d7
Binary files /dev/null and b/docs/fonts/Roboto/Roboto-Regular-webfont.woff differ
diff --git a/docs/fonts/Roboto/stylesheet.css b/docs/fonts/Roboto/stylesheet.css
new file mode 100755
index 0000000..dd6d4c4
--- /dev/null
+++ b/docs/fonts/Roboto/stylesheet.css
@@ -0,0 +1,13 @@
@font-face {
    font-family: 'robotoregular';
    src: url('Roboto-Regular-webfont.eot');
    src: url('Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Regular-webfont.woff2') format('woff2'),
         url('Roboto-Regular-webfont.woff') format('woff'),
         url('Roboto-Regular-webfont.ttf') format('truetype'),
         url('Roboto-Regular-webfont.svg#robotoregular') format('svg');
    font-weight: normal;
    font-style: normal;

}

diff --git a/docs/index.php b/docs/index.php
new file mode 100755
index 0000000..c998c88
--- /dev/null
+++ b/docs/index.php
@@ -0,0 +1,23 @@
<?php
    require $_SERVER['DOCUMENT_ROOT'] . '/vendor/parsedown/parsedown.php';
?>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width initial-scale=1">
<title>Open Source License API Documentation</title>
<style>
<?php echo(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/docs/stylesheet.css')); ?>
<?php /* Uncomment for dark theme echo(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/docs/stylesheet-dark.css')); */ ?>

</style>
</head>
<body>
    <?php
        $Parsedown = new Parsedown();
        echo $Parsedown->text(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/DOCUMENTATION.md'));

    ?>
</body>
</html>
\ No newline at end of file
diff --git a/docs/stylesheet-dark.css b/docs/stylesheet-dark.css
new file mode 100755
index 0000000..b03a75a
--- /dev/null
+++ b/docs/stylesheet-dark.css
@@ -0,0 +1,433 @@
@font-face {
    font-family: 'Roboto';
    src: url('/docs/fonts/Roboto/Roboto-Regular-webfont.eot');
    src: url('/docs/fonts/Roboto/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.woff2') format('woff2'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.woff') format('woff'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.ttf') format('truetype'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.svg#robotoregular') format('svg');
    font-weight: normal;
    font-style: normal;
  }

a[href] {
  color: #8ac;
  text-decoration: none;
}

a[href] img {
  /* hide link underline */
  box-shadow: 0 10px 0 #fff;
}

/*
    Buttondown
    A Markdown/MultiMarkdown/Pandoc HTML output CSS stylesheet
    Author: Ryan Gray
    Date: 15 Feb 2011
    Revised: 21 Feb 2012
   
    General style is clean, with minimal re-definition of the defaults or 
    overrides of user font settings. The body text and header styles are 
    left alone except title, author and date classes are centered. A Pandoc TOC 
    is not printed, URLs are printed after hyperlinks in parentheses. 
    Block quotes are italicized. Tables are lightly styled with lines above 
    and below the table and below the header with a boldface header. Code 
    blocks are line wrapped. 
    All elements that Pandoc and MultiMarkdown use should be listed here, even 
    if the style is empty so you can easily add styling to anything.
    
    There are some elements in here for HTML5 output of Pandoc, but I have not 
    gotten around to testing that yet.
*/

/* NOTES:
    Stuff tried and failed:
    
    It seems that specifying font-family:serif in Safari will always use 
    Times New Roman rather than the user's preferences setting.
    
    Making the font size different or a fixed value for print in case the screen 
    font size is making the print font too big: Making font-size different for 
    print than for screen causes horizontal lines to disappear in math when using 
    MathJax under Safari.
*/

/* ---- Front Matter ---- */

/* Pandoc header DIV. Contains .title, .author and .date. Comes before div#TOC. 
   Only appears if one of those three are in the document.
*/

div#header, header
    {
    /* Put border on bottom. Separates it from TOC or body that comes after it. */
    border-bottom: 1px solid #aaa;
    margin-bottom: 0.5em;
    }

.title /* Pandoc title header (h1.title) */
    {
    text-align: center;
    }

.author, .date /* Pandoc author(s) and date headers (h2.author and h3.date) */
    {
    text-align: center;
    }

/* Pandoc table of contents DIV when using the --toc option.
   NOTE: this doesn't support Pandoc's --id-prefix option for #TOC and #header. 
   Probably would need to use div[id$='TOC'] and div[id$='header'] as selectors.
*/

div#TOC, nav#TOC
    {
    /* Put border on bottom to separate it from body. */
    border-bottom: 1px solid #aaa;
    margin-bottom: 0.5em;
    }

@media print
    {
    div#TOC, nav#TOC
        {
        /* Don't display TOC in print */
        display: none;
        }
    }

/* ---- Body ---- */
body
{
    font-family: "Roboto";
    background: #191919;
    color: #aaa;
}

/* ---- Headers and sections ---- */

h1, h2, h3, h4, h5, h6
{
    /* font-family: "Liberation Serif", "Georgia", "Times New Roman", serif; /* Serif headers */

    page-break-after: avoid; /* Firefox, Chrome, and Safari do not support the property value "avoid" */
}

/* Pandoc with --section-divs option */

div div, section section /* Nested sections */
    {
    margin-left: 2em; /* This will increasingly indent nested header sections */
    }

p {}

blockquote
    { 
    font-style: italic;
    }

li /* All list items */
    {
    }

li > p /* Loosely spaced list item */
    {
    margin-top: 1em; /* IE: lack of space above a <li> when the item is inside a <p> */
    }

ul /* Whole unordered list */
    {
    }

ul li /* Unordered list item */
    {
    }

ol /* Whole ordered list */
    {
    }

ol li /* Ordered list item */
    {
    }

hr {
    border-color: #333;
    background-color: #333;
    color: #333;
    height: .3rem;
    border: 0 none;
}

/* ---- Some span elements --- */

sub /* Subscripts. Pandoc: H~2~O */
    {
    }

sup /* Superscripts. Pandoc: The 2^nd^ try. */
    {
    }
    
em /* Emphasis. Markdown: *emphasis* or _emphasis_ */
    {
    }
    
em > em /* Emphasis within emphasis: *This is all *emphasized* except that* */
    {
    font-style: normal;
    }

strong /* Markdown **strong** or __strong__ */
    {
    }

/* ---- Links (anchors) ---- */

a /* All links */
    {
    /* Keep links clean. On screen, they are colored; in print, they do nothing anyway. */
    text-decoration: none;
    }

@media screen
    {
    a:hover
        {
        /* On hover, we indicate a bit more that it is a link. */
        text-decoration: underline;
        }
    }

@media print
    {
    a   {
        /* In print, a colored link is useless, so un-style it. */
        color: black;
        background: transparent;
        }
        
    a[href^="http://"]:after, a[href^="https://"]:after
        {
        /* However, links that go somewhere else, might be useful to the reader,
           so for http and https links, print the URL after what was the link 
           text in parens
        */
        content: " (" attr(href) ") ";
        font-size: 90%;
        }
    }

/* ---- Images ---- */

img
    {
    /* Let it be inline left/right where it wants to be, but verticality make 
       it in the middle to look nicer, but opinions differ, and if in a multi-line 
       paragraph, it might not be so great. 
    */
    vertical-align: middle;
    }

div.figure /* Pandoc figure-style image */
    {
    /* Center the image and caption */
    margin-left: auto;
    margin-right: auto;
    text-align: center;
    font-style: italic;
    }

p.caption /* Pandoc figure-style caption within div.figure */
    {
    /* Inherits div.figure props by default */
    }

/* ---- Code blocks and spans ---- */

pre, code 
    {
    background-color: #292929;
    /* BEGIN word wrap */
    /* Need all the following to word wrap instead of scroll box */
    /* This will override the overflow:auto if present */
    white-space: pre-wrap; /* css-3 */
    white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
    white-space: -pre-wrap; /* Opera 4-6 */
    white-space: -o-pre-wrap; /* Opera 7 */
    word-wrap: break-word; /* Internet Explorer 5.5+ */
    /* END word wrap */
    }

pre /* Code blocks */
    {
    /* Distinguish pre blocks from other text by more than the font with a background tint. */
    padding: 0.5em; /* Since we have a background color */
    border-radius: 0px; /* Softens it */
    /* Give it a some definition */
    border: 1px solid #292929;
    /* Set it off left and right, seems to look a bit nicer when we have a background */
    margin-left:  0.5em;
    margin-right: 0.5em;
    }

@media screen
    {
    pre
        {
        /* On screen, use an auto scroll box for long lines, unless word-wrap is enabled */
        white-space: pre;
        overflow: auto;
        }
    }

code /* All inline code spans */
    {
    }

p > code, li > code /* Code spans in paragraphs and tight lists */
    {
    /* Pad a little from adjacent text */
    padding-left:  2px;
    padding-right: 2px;
    }
    
li > p code /* Code span in a loose list */
    {
    /* We have room for some more background color above and below */
    padding: 2px;
    }

/* ---- Math ---- */

span.math /* Pandoc inline math default and --jsmath inline math */
    {
    /* Tried font-style:italic here, and it messed up MathJax rendering in some browsers. Maybe don't mess with at all. */
    }
    
div.math /* Pandoc --jsmath display math */
    {
    }
    
span.LaTeX /* Pandoc --latexmathml math */
    {
    } 

eq /* Pandoc --gladtex math */
    {
    } 

/* ---- Tables ---- */

/*  A clean textbook-like style with horizontal lines above and below and under 
    the header. Rows highlight on hover to help scanning the table on screen.
*/

table {
  border-collapse: collapse;
  border-top: .3rem solid #333;
  margin-bottom:  1.9rem;
  width: 100%;
}

table td,
table th {
  padding: calc( 1.9rem / 4) .6rem;
}

table td:first-child,
table th:first-child {
  padding-left: 0;
}

table td:last-child,
table th:last-child {
  padding-right: 0;
}

table th {
  font-weight: normal;
  text-align: left;
}

table tbody tr:first-child {
  border-top: .2rem solid #333;
}

table tbody tr:last-child {
  border-bottom: .3rem solid #333;
}

/* ---- Definition lists ---- */

dl /* The whole list */
    {
    border-top: 2pt solid black;
    padding-top: 0.5em;
    border-bottom: 2pt solid black;
    }

dt /* Definition term */
    {
    font-weight: bold;
    }

dd+dt /* 2nd or greater term in the list */
    {
    border-top: 1pt solid black;
    padding-top: 0.5em;
    }
    
dd /* A definition */
    {
    margin-bottom: 0.5em;
    }

dd+dd /* 2nd or greater definition of a term */
    {
    border-top: 1px solid black; /* To separate multiple definitions */
    }
    
/* ---- Footnotes ---- */

a.footnote, a.footnoteRef { /* Pandoc, MultiMarkdown footnote links */
    font-size: small; 
    vertical-align: text-top;
}

a[href^="#fnref"], a.reversefootnote /* Pandoc, MultiMarkdown, ?? footnote back links */
    {
    }

@media print
    {
    a[href^="#fnref"], a.reversefootnote /* Pandoc, MultiMarkdown */
        {
        /* Don't display these at all in print since the arrow is only something to click on */
        display: none;
        }
    }
    
div.footnotes /* Pandoc footnotes div at end of the document */
    {
    }
    
div.footnotes li[id^="fn"] /* A footnote item within that div */
    {
    }

/* You can class stuff as "noprint" to not print. 
   Useful since you can't set this media conditional inside an HTML element's 
   style attribute (I think), and you don't want to make another stylesheet that 
   imports this one and adds a class just to do this.
*/

@media print
    {
    .noprint
        {
        display:none;
        }
    }
\ No newline at end of file
diff --git a/docs/stylesheet.css b/docs/stylesheet.css
new file mode 100755
index 0000000..b953348
--- /dev/null
+++ b/docs/stylesheet.css
@@ -0,0 +1,433 @@
@font-face {
    font-family: 'Roboto';
    src: url('/docs/fonts/Roboto/Roboto-Regular-webfont.eot');
    src: url('/docs/fonts/Roboto/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.woff2') format('woff2'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.woff') format('woff'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.ttf') format('truetype'),
         url('/docs/fonts/Roboto/Roboto-Regular-webfont.svg#robotoregular') format('svg');
    font-weight: normal;
    font-style: normal;
  }

a[href] {
  color: #7799BB;
  text-decoration: none;
}

a[href] img {
  /* hide link underline */
  box-shadow: 0 10px 0 #fff;
}

/*
    Buttondown
    A Markdown/MultiMarkdown/Pandoc HTML output CSS stylesheet
    Author: Ryan Gray
    Date: 15 Feb 2011
    Revised: 21 Feb 2012
   
    General style is clean, with minimal re-definition of the defaults or 
    overrides of user font settings. The body text and header styles are 
    left alone except title, author and date classes are centered. A Pandoc TOC 
    is not printed, URLs are printed after hyperlinks in parentheses. 
    Block quotes are italicized. Tables are lightly styled with lines above 
    and below the table and below the header with a boldface header. Code 
    blocks are line wrapped. 
    All elements that Pandoc and MultiMarkdown use should be listed here, even 
    if the style is empty so you can easily add styling to anything.
    
    There are some elements in here for HTML5 output of Pandoc, but I have not 
    gotten around to testing that yet.
*/

/* NOTES:
    Stuff tried and failed:
    
    It seems that specifying font-family:serif in Safari will always use 
    Times New Roman rather than the user's preferences setting.
    
    Making the font size different or a fixed value for print in case the screen 
    font size is making the print font too big: Making font-size different for 
    print than for screen causes horizontal lines to disappear in math when using 
    MathJax under Safari.
*/

/* ---- Front Matter ---- */

/* Pandoc header DIV. Contains .title, .author and .date. Comes before div#TOC. 
   Only appears if one of those three are in the document.
*/

div#header, header
    {
    /* Put border on bottom. Separates it from TOC or body that comes after it. */
    border-bottom: 1px solid #aaa;
    margin-bottom: 0.5em;
    }

.title /* Pandoc title header (h1.title) */
    {
    text-align: center;
    }

.author, .date /* Pandoc author(s) and date headers (h2.author and h3.date) */
    {
    text-align: center;
    }

/* Pandoc table of contents DIV when using the --toc option.
   NOTE: this doesn't support Pandoc's --id-prefix option for #TOC and #header. 
   Probably would need to use div[id$='TOC'] and div[id$='header'] as selectors.
*/

div#TOC, nav#TOC
    {
    /* Put border on bottom to separate it from body. */
    border-bottom: 1px solid #aaa;
    margin-bottom: 0.5em;
    }

@media print
    {
    div#TOC, nav#TOC
        {
        /* Don't display TOC in print */
        display: none;
        }
    }

/* ---- Body ---- */
body
{
    font-family: "Roboto";
    background: #FFFFFF;
    color: #4B4B4B;
}

/* ---- Headers and sections ---- */

h1, h2, h3, h4, h5, h6
{
    /* font-family: "Liberation Serif", "Georgia", "Times New Roman", serif; /* Serif headers */

    page-break-after: avoid; /* Firefox, Chrome, and Safari do not support the property value "avoid" */
}

/* Pandoc with --section-divs option */

div div, section section /* Nested sections */
    {
    margin-left: 2em; /* This will increasingly indent nested header sections */
    }

p {}

blockquote
    { 
    font-style: italic;
    }

li /* All list items */
    {
    }

li > p /* Loosely spaced list item */
    {
    margin-top: 1em; /* IE: lack of space above a <li> when the item is inside a <p> */
    }

ul /* Whole unordered list */
    {
    }

ul li /* Unordered list item */
    {
    }

ol /* Whole ordered list */
    {
    }

ol li /* Ordered list item */
    {
    }

hr {
    border-color: #DDDDDD;
    background-color: #DDDDDD;
    color: #DDDDDD;
    height: .3rem;
    border: 0 none;
}

/* ---- Some span elements --- */

sub /* Subscripts. Pandoc: H~2~O */
    {
    }

sup /* Superscripts. Pandoc: The 2^nd^ try. */
    {
    }
    
em /* Emphasis. Markdown: *emphasis* or _emphasis_ */
    {
    }
    
em > em /* Emphasis within emphasis: *This is all *emphasized* except that* */
    {
    font-style: normal;
    }

strong /* Markdown **strong** or __strong__ */
    {
    }

/* ---- Links (anchors) ---- */

a /* All links */
    {
    /* Keep links clean. On screen, they are colored; in print, they do nothing anyway. */
    text-decoration: none;
    }

@media screen
    {
    a:hover
        {
        /* On hover, we indicate a bit more that it is a link. */
        text-decoration: underline;
        }
    }

@media print
    {
    a   {
        /* In print, a colored link is useless, so un-style it. */
        color: black;
        background: transparent;
        }
        
    a[href^="http://"]:after, a[href^="https://"]:after
        {
        /* However, links that go somewhere else, might be useful to the reader,
           so for http and https links, print the URL after what was the link 
           text in parens
        */
        content: " (" attr(href) ") ";
        font-size: 90%;
        }
    }

/* ---- Images ---- */

img
    {
    /* Let it be inline left/right where it wants to be, but verticality make 
       it in the middle to look nicer, but opinions differ, and if in a multi-line 
       paragraph, it might not be so great. 
    */
    vertical-align: middle;
    }

div.figure /* Pandoc figure-style image */
    {
    /* Center the image and caption */
    margin-left: auto;
    margin-right: auto;
    text-align: center;
    font-style: italic;
    }

p.caption /* Pandoc figure-style caption within div.figure */
    {
    /* Inherits div.figure props by default */
    }

/* ---- Code blocks and spans ---- */

pre, code 
    {
    background-color: #F3F3F3;
    /* BEGIN word wrap */
    /* Need all the following to word wrap instead of scroll box */
    /* This will override the overflow:auto if present */
    white-space: pre-wrap; /* css-3 */
    white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
    white-space: -pre-wrap; /* Opera 4-6 */
    white-space: -o-pre-wrap; /* Opera 7 */
    word-wrap: break-word; /* Internet Explorer 5.5+ */
    /* END word wrap */
    }

pre /* Code blocks */
    {
    /* Distinguish pre blocks from other text by more than the font with a background tint. */
    padding: 0.5em; /* Since we have a background color */
    border-radius: 0px; /* Softens it */
    /* Give it a some definition */
    border: 1px solid #F3F3F3;
    /* Set it off left and right, seems to look a bit nicer when we have a background */
    margin-left:  0.5em;
    margin-right: 0.5em;
    }

@media screen
    {
    pre
        {
        /* On screen, use an auto scroll box for long lines, unless word-wrap is enabled */
        white-space: pre;
        overflow: auto;
        }
    }

code /* All inline code spans */
    {
    }

p > code, li > code /* Code spans in paragraphs and tight lists */
    {
    /* Pad a little from adjacent text */
    padding-left:  2px;
    padding-right: 2px;
    }
    
li > p code /* Code span in a loose list */
    {
    /* We have room for some more background color above and below */
    padding: 2px;
    }

/* ---- Math ---- */

span.math /* Pandoc inline math default and --jsmath inline math */
    {
    /* Tried font-style:italic here, and it messed up MathJax rendering in some browsers. Maybe don't mess with at all. */
    }
    
div.math /* Pandoc --jsmath display math */
    {
    }
    
span.LaTeX /* Pandoc --latexmathml math */
    {
    } 

eq /* Pandoc --gladtex math */
    {
    } 

/* ---- Tables ---- */

/*  A clean textbook-like style with horizontal lines above and below and under 
    the header. Rows highlight on hover to help scanning the table on screen.
*/

table {
  border-collapse: collapse;
  border-top: .3rem solid #DDDDDD;
  margin-bottom:  1.9rem;
  width: 100%;
}

table td,
table th {
  padding: calc( 1.9rem / 4) .6rem;
}

table td:first-child,
table th:first-child {
  padding-left: 0;
}

table td:last-child,
table th:last-child {
  padding-right: 0;
}

table th {
  font-weight: normal;
  text-align: left;
}

table tbody tr:first-child {
  border-top: .2rem solid #DDDDDD;
}

table tbody tr:last-child {
  border-bottom: .3rem solid #DDDDDD;
}

/* ---- Definition lists ---- */

dl /* The whole list */
    {
    border-top: 2pt solid black;
    padding-top: 0.5em;
    border-bottom: 2pt solid black;
    }

dt /* Definition term */
    {
    font-weight: bold;
    }

dd+dt /* 2nd or greater term in the list */
    {
    border-top: 1pt solid black;
    padding-top: 0.5em;
    }
    
dd /* A definition */
    {
    margin-bottom: 0.5em;
    }

dd+dd /* 2nd or greater definition of a term */
    {
    border-top: 1px solid black; /* To separate multiple definitions */
    }
    
/* ---- Footnotes ---- */

a.footnote, a.footnoteRef { /* Pandoc, MultiMarkdown footnote links */
    font-size: small; 
    vertical-align: text-top;
}

a[href^="#fnref"], a.reversefootnote /* Pandoc, MultiMarkdown, ?? footnote back links */
    {
    }

@media print
    {
    a[href^="#fnref"], a.reversefootnote /* Pandoc, MultiMarkdown */
        {
        /* Don't display these at all in print since the arrow is only something to click on */
        display: none;
        }
    }
    
div.footnotes /* Pandoc footnotes div at end of the document */
    {
    }
    
div.footnotes li[id^="fn"] /* A footnote item within that div */
    {
    }

/* You can class stuff as "noprint" to not print. 
   Useful since you can't set this media conditional inside an HTML element's 
   style attribute (I think), and you don't want to make another stylesheet that 
   imports this one and adds a class just to do this.
*/

@media print
    {
    .noprint
        {
        display:none;
        }
    }
\ No newline at end of file
diff --git a/errorpages/403.php b/errorpages/403.php
new file mode 100755
index 0000000..2624c71
--- /dev/null
+++ b/errorpages/403.php
@@ -0,0 +1,18 @@
<?php
    require $_SERVER['DOCUMENT_ROOT'] . '/vendor/parsedown/parsedown.php';
?>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width initial-scale=1">
<title>Forbidden</title>
</head>
<body>
    <?php
        $Parsedown = new Parsedown();
        echo $Parsedown->text(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/errorpages/error-403.md'));

    ?>
</body>
</html>
\ No newline at end of file
diff --git a/errorpages/404.php b/errorpages/404.php
new file mode 100755
index 0000000..bfd7020
--- /dev/null
+++ b/errorpages/404.php
@@ -0,0 +1,18 @@
<?php
    require $_SERVER['DOCUMENT_ROOT'] . '/vendor/parsedown/parsedown.php';
?>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width initial-scale=1">
<title>Not found</title>
</head>
<body>
    <?php
        $Parsedown = new Parsedown();
        echo $Parsedown->text(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/errorpages/error-404.md'));

    ?>
</body>
</html>
\ No newline at end of file
diff --git a/errorpages/error-403.md b/errorpages/error-403.md
new file mode 100755
index 0000000..416ec04
--- /dev/null
+++ b/errorpages/error-403.md
@@ -0,0 +1,2 @@
# 403
## Forbidden.
\ No newline at end of file
diff --git a/errorpages/error-404.md b/errorpages/error-404.md
new file mode 100755
index 0000000..f8fce0e
--- /dev/null
+++ b/errorpages/error-404.md
@@ -0,0 +1,2 @@
# 404
## File not found.
\ No newline at end of file
diff --git a/functions.php b/functions.php
new file mode 100755
index 0000000..7999651
--- /dev/null
+++ b/functions.php
@@ -0,0 +1,133 @@
<?php

    /**
     * Returns a list of available endpoints
     */
    function get_endpoints()
    {
        $endpoints = './licenses/endpoints.txt';
        if (file_exists($endpoints))
        {
            // Send response code 200 OK and the content type
            http_response_code(200);
            header("Content-type: text/plain");

            //Print the contents of the endpoints list
            readfile($endpoints);
        }
        else
        {
            // Send response code 503 service unavailable and the content type
            http_response_code(503);
            header("Content-type: text/plain");

            // Let the user know to contact admins since there is an issue
            echo("ERROR - Endpoint list not found! Contact the site administrator if the problem persists.");
        }
    }

    /**
     * Returns a list of available license IDs. Limited to 5 licenses per row for readability.
     */
    function get_list()
    {
        $database = './licenses/licenses.txt';
        if (file_exists($database))
        {
            // Send response code 200 OK and the content type
            http_response_code(200);
            header("Content-type: text/plain");

            // Get the file contents
            $contents = file($database);
            $displayed_text = "";

            $max_per_line = 5; // Limit to 5 licenses per line to improve readability in terminals
            $current_item = 0; // Index of license on current line
            foreach ($contents as $line_num => $line) 
            {
                //Only append text before the first space
                $id = strtok($line, ' ');
                $displayed_text .= $id . ', ';
                
                //Move to the next index
                ++$current_item;
                if ($current_item == $max_per_line)
                {
                    // We hit the maximum number of items on this row, so move to the next line
                    $displayed_text .= "\n";
                    $current_item = 0;
                }
            }

            // Remove the last comma and add a newline
            $displayed_text = rtrim($displayed_text, ", ");
            $displayed_text .= "\n";

            echo $displayed_text;
        }
        else
        {
            // Send response code 503 service unavailable and the content type
            http_response_code(503);
            header("Content-type: text/plain");

            // Let the user know to contact admins since there is an issue
            echo("ERROR - License database not found! Contact the site administrator if the problem persists.");
        }
    }

    /**
     * Returns a detailed list of available licenses
     */
    function get_list_full()
    {
        $database = './licenses/licenses.txt';
        if (file_exists($database))
        {
            // Send response code 200 OK and the content type
            http_response_code(200);
            header('Content-Type: text/plain');

            // Print the contents of the pre-generated license list
            readfile($database);
        }
        else
        {
            // Send response code 503 service unavailable and the content type
            http_response_code(503);
            header("Content-type: text/plain");

            // Let the user know to contact admins since there is an issue
            echo("ERROR - License database not found! Contact the site administrator if the problem persists.");
        }
    }

    /**
     * Returns a single license using its ID
     */
    function get_license($license)
    {
        if (file_exists($license))
        {
            // Send response code 200 OK and the content type
            http_response_code(200);
            header("Content-type: text/plain");

            //Print the contents of the license file
            readfile($license);
            //Add a new line to the end of the output
            echo ("\n");
        }
        else
        {
            // Send response code 404 not found and the content type
            http_response_code(404);
            header("Content-type: text/plain");

            // Let the user know that the requested license is not found
            echo("ERROR - " . strtolower($license) . " is undefined. Use list command to see defined license types.");
        }
    }

?>
\ No newline at end of file
diff --git a/index.php b/index.php
new file mode 100755
index 0000000..ab43cc2
--- /dev/null
+++ b/index.php
@@ -0,0 +1,53 @@
<?php
    //Include our API functions
    include('./functions.php');

    // Kickstart the framework
    $f3=require('./vendor/fatfree-3.6/lib/base.php');

    $f3->route('GET /',
        function() 
        {
            $page = $_SERVER['DOCUMENT_ROOT'] . '/docs/index.php';
            if (file_exists($page))
            {
                include($_SERVER['DOCUMENT_ROOT'] . '/docs/index.php');
            }
            else
            {
                echo('No default page defined.');
            }
        }
    );

    $f3->route('GET /api',
        function() 
        {
            get_endpoints();
        }
    );

    $f3->route('GET /api/list',
        function() 
        {
            get_list();
        }
    );

    $f3->route('GET /api/list/full',
        function() 
        {
            get_list_full();
        }
    );

    $f3->route('GET /api/@license',
        function($f3) 
        {
            $license = "./licenses/raw/" . strtolower($f3->get('PARAMS.license')) . '.txt';
            get_license($license);
        }
    );

    $f3->run();
?>
\ No newline at end of file
diff --git a/licenses/endpoints.txt b/licenses/endpoints.txt
new file mode 100755
index 0000000..5d9fe96
--- /dev/null
+++ b/licenses/endpoints.txt
@@ -0,0 +1,15 @@
===============================================================
                      Available Endpoints                      
===============================================================
   Endpoint: /api/
Description: List all available endpoints
-
   Endpoint: /api/list
Description: List IDs of all available licenses
-
   Endpoint: /api/list/full
Description: List IDs and names of all available licenses
-
   Endpoint: /api/{license}
Description: Retrieve a license using its ID
===============================================================
diff --git a/licenses/licenses.txt b/licenses/licenses.txt
new file mode 100755
index 0000000..2a8b5b3
--- /dev/null
+++ b/licenses/licenses.txt
@@ -0,0 +1,34 @@
afl-3.0             Academic Free License v3.0
agpl-3.0            GNU Affero General Public License v3.0
apache-2.0          Apache License 2.0
artistic-2.0        Artistic License 2.0
bsd-2-clause        BSD 2-Clause "Simplified" License
bsd-3-clause-clear  BSD 3-Clause Clear License
bsd-3-clause        BSD 3-Clause "New" or "Revised" License
bsl-1.0             Boost Software License 1.0
cc-by-4.0           Creative Commons Attribution 4.0 International
cc-by-sa-4.0        Creative Commons Attribution Share Alike 4.0 International
cc0-1.0             Creative Commons Zero v1.0 Universal
ecl-2.0             Educational Community License v2.0
epl-1.0             Eclipse Public License 1.0
epl-2.0             Eclipse Public License 2.0
eupl-1.1            European Union Public License 1.1
eupl-1.2            European Union Public License 1.2
gpl-2.0             GNU General Public License v2.0
gpl-3.0             GNU General Public License v3.0
isc                 ISC License
lgpl-2.1            GNU Lesser General Public License v2.1
lgpl-3.0            GNU Lesser General Public License v3.0
lppl-1.3c           LaTeX Project Public License v1.3c
mit                 MIT License
mpl-2.0             Mozilla Public License 2.0
ms-pl               Microsoft Public License
ms-rl               Microsoft Reciprocal License
ncsa                University of Illinois/NCSA Open Source License
ofl-1.1             SIL Open Font License 1.1
osl-3.0             Open Software License 3.0
postgresql          PostgreSQL License
unlicense           The Unlicense
upl-1.0             Universal Permissive License v1.0
wtfpl               Do What The F*ck You Want To Public License
zlib                zlib License
diff --git a/licenses/raw/afl-3.0.txt b/licenses/raw/afl-3.0.txt
new file mode 100755
index 0000000..859074b
--- /dev/null
+++ b/licenses/raw/afl-3.0.txt
@@ -0,0 +1,170 @@
Academic Free License (“AFL”) v. 3.0

This Academic Free License (the "License") applies to any original work of
authorship (the "Original Work") whose owner (the "Licensor") has placed the
following licensing notice adjacent to the copyright notice for the Original
Work:

     Licensed under the Academic Free License version 3.0

1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, for the duration of the copyright, to do
the following:

     a) to reproduce the Original Work in copies, either alone or as part of a
     collective work;

     b) to translate, adapt, alter, transform, modify, or arrange the Original
     Work, thereby creating derivative works ("Derivative Works") based upon
     the Original Work;

     c) to distribute or communicate copies of the Original Work and
     Derivative Works to the public, under any license of your choice that
     does not contradict the terms and conditions, including Licensor’s
     reserved rights and remedies, in this Academic Free License;
     d) to perform the Original Work publicly; and
     e) to display the Original Work publicly.

2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, under patent claims owned or controlled
by the Licensor that are embodied in the Original Work as furnished by the
Licensor, for the duration of the patents, to make, use, sell, offer for sale,
have made, and import the Original Work and Derivative Works.

3) Grant of Source Code License. The term "Source Code" means the preferred
form of the Original Work for making modifications to it and all available
documentation describing how to modify the Original Work. Licensor agrees to
provide a machine-readable copy of the Source Code of the Original Work along
with each copy of the Original Work that Licensor distributes. Licensor
reserves the right to satisfy this obligation by placing a machine-readable
copy of the Source Code in an information repository reasonably calculated to
permit inexpensive and convenient access by You for as long as Licensor
continues to distribute the Original Work.

4) Exclusions From License Grant. Neither the names of Licensor, nor the names
of any contributors to the Original Work, nor any of their trademarks or
service marks, may be used to endorse or promote products derived from this
Original Work without express prior permission of the Licensor. Except as
expressly stated herein, nothing in this License grants any license to
Licensor’s trademarks, copyrights, patents, trade secrets or any other
intellectual property. No patent license is granted to make, use, sell, offer
for sale, have made, or import embodiments of any patent claims other than the
licensed claims defined in Section 2. No license is granted to the trademarks
of Licensor even if such marks are included in the Original Work. Nothing in
this License shall be interpreted to prohibit Licensor from licensing under
terms different from this License any Original Work that Licensor otherwise
would have a right to license.

5) External Deployment. The term "External Deployment" means the use,
distribution, or communication of the Original Work or Derivative Works in any
way such that the Original Work or Derivative Works may be used by anyone
other than You, whether those works are distributed or communicated to those
persons or made available as an application intended for use over a network.
As an express condition for the grants of license hereunder, You must treat
any External Deployment by You of the Original Work or a Derivative Work as a
distribution under section 1(c).

6) Attribution Rights. You must retain, in the Source Code of any Derivative
Works that You create, all copyright, patent, or trademark notices from the
Source Code of the Original Work, as well as any notices of licensing and any
descriptive text identified therein as an "Attribution Notice." You must cause
the Source Code for any Derivative Works that You create to carry a prominent
Attribution Notice reasonably calculated to inform recipients that You have
modified the Original Work.

7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
the copyright in and to the Original Work and the patent rights granted herein
by Licensor are owned by the Licensor or are sublicensed to You under the
terms of this License with the permission of the contributor(s) of those
copyrights and patent rights. Except as expressly stated in the immediately
preceding sentence, the Original Work is provided under this License on an "AS
IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
limitation, the warranties of non-infringement, merchantability or fitness for
a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK
IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
License. No license to the Original Work is granted by this License except
under this disclaimer.

8) Limitation of Liability. Under no circumstances and under no legal theory,
whether in tort (including negligence), contract, or otherwise, shall the
Licensor be liable to anyone for any indirect, special, incidental, or
consequential damages of any character arising as a result of this License or
the use of the Original Work including, without limitation, damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses. This limitation of liability shall not
apply to the extent applicable law prohibits such limitation.

9) Acceptance and Termination. If, at any time, You expressly assented to this
License, that assent indicates your clear and irrevocable acceptance of this
License and all of its terms and conditions. If You distribute or communicate
copies of the Original Work or a Derivative Work, You must make a reasonable
effort under the circumstances to obtain the express assent of recipients to
the terms of this License. This License conditions your rights to undertake
the activities listed in Section 1, including your right to create Derivative
Works based upon the Original Work, and doing so without honoring these terms
and conditions is prohibited by copyright law and international treaty.
Nothing in this License is intended to affect copyright exceptions and
limitations (including “fair use” or “fair dealing”). This License shall
terminate immediately and You may no longer exercise any of the rights granted
to You by this License upon your failure to honor the conditions in Section
1(c).

10) Termination for Patent Action. This License shall terminate automatically
and You may no longer exercise any of the rights granted to You by this
License as of the date You commence an action, including a cross-claim or
counterclaim, against Licensor or any licensee alleging that the Original Work
infringes a patent. This termination provision shall not apply for an action
alleging patent infringement by combinations of the Original Work with other
software or hardware.

11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
License may be brought only in the courts of a jurisdiction wherein the
Licensor resides or in which Licensor conducts its primary business, and under
the laws of that jurisdiction excluding its conflict-of-law provisions. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded. Any use of the Original
Work outside the scope of this License or after its termination shall be
subject to the requirements and penalties of copyright or patent law in the
appropriate jurisdiction. This section shall survive the termination of this
License.

12) Attorneys’ Fees. In any action to enforce the terms of this License or
seeking damages relating thereto, the prevailing party shall be entitled to
recover its costs and expenses, including, without limitation, reasonable
attorneys' fees and costs incurred in connection with such action, including
any appeal of such action. This section shall survive the termination of this
License.

13) Miscellaneous. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent necessary
to make it enforceable.

14) Definition of "You" in This License. "You" throughout this License,
whether in upper or lower case, means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this License.
For legal entities, "You" includes any entity that controls, is controlled by,
or is under common control with you. For purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or (ii) ownership
of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.

15) Right to Use. You may use the Original Work in all ways not otherwise
restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You.

16) Modification of This License. This License is Copyright © 2005 Lawrence
Rosen. Permission is granted to copy, distribute, or communicate this License
without modification. Nothing in this License permits You to modify this
License as applied to the Original Work or to Derivative Works. However, You
may modify the text of this License and copy, distribute or communicate your
modified version (the "Modified License") and apply it to other original works
of authorship subject to the following conditions: (i) You may not indicate in
any way that your Modified License is the "Academic Free License" or "AFL" and
you may not use those names in the name of your Modified License; (ii) You
must replace the notice specified in the first paragraph above with the notice
"Licensed under <insert your license name here>" or with a notice of your own
that is not confusingly similar to the notice in this License; and (iii) You
may not claim that your original works are open source software unless your
Modified License has been approved by Open Source Initiative (OSI) and You
comply with its license review and certification process.
\ No newline at end of file
diff --git a/licenses/raw/agpl-3.0.txt b/licenses/raw/agpl-3.0.txt
new file mode 100755
index 0000000..1ce8758
--- /dev/null
+++ b/licenses/raw/agpl-3.0.txt
@@ -0,0 +1,661 @@
                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/licenses/raw/apache-2.0.txt b/licenses/raw/apache-2.0.txt
new file mode 100755
index 0000000..f49a4e1
--- /dev/null
+++ b/licenses/raw/apache-2.0.txt
@@ -0,0 +1,201 @@
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
\ No newline at end of file
diff --git a/licenses/raw/artistic-2.0.txt b/licenses/raw/artistic-2.0.txt
new file mode 100755
index 0000000..209a7ea
--- /dev/null
+++ b/licenses/raw/artistic-2.0.txt
@@ -0,0 +1,201 @@
		       The Artistic License 2.0

	    Copyright (c) 2000-2006, The Perl Foundation.

     Everyone is permitted to copy and distribute verbatim copies
      of this license document, but changing it is not allowed.

Preamble

This license establishes the terms under which a given free software
Package may be copied, modified, distributed, and/or redistributed.
The intent is that the Copyright Holder maintains some artistic
control over the development of that Package while still keeping the
Package available as open source and free software.

You are always permitted to make arrangements wholly outside of this
license directly with the Copyright Holder of a given Package.  If the
terms of this license do not permit the full use that you propose to
make of the Package, you should contact the Copyright Holder and seek
a different licensing arrangement.

Definitions

    "Copyright Holder" means the individual(s) or organization(s)
    named in the copyright notice for the entire Package.

    "Contributor" means any party that has contributed code or other
    material to the Package, in accordance with the Copyright Holder's
    procedures.

    "You" and "your" means any person who would like to copy,
    distribute, or modify the Package.

    "Package" means the collection of files distributed by the
    Copyright Holder, and derivatives of that collection and/or of
    those files. A given Package may consist of either the Standard
    Version, or a Modified Version.

    "Distribute" means providing a copy of the Package or making it
    accessible to anyone else, or in the case of a company or
    organization, to others outside of your company or organization.

    "Distributor Fee" means any fee that you charge for Distributing
    this Package or providing support for this Package to another
    party.  It does not mean licensing fees.

    "Standard Version" refers to the Package if it has not been
    modified, or has been modified only in ways explicitly requested
    by the Copyright Holder.

    "Modified Version" means the Package, if it has been changed, and
    such changes were not explicitly requested by the Copyright
    Holder.

    "Original License" means this Artistic License as Distributed with
    the Standard Version of the Package, in its current version or as
    it may be modified by The Perl Foundation in the future.

    "Source" form means the source code, documentation source, and
    configuration files for the Package.

    "Compiled" form means the compiled bytecode, object code, binary,
    or any other form resulting from mechanical transformation or
    translation of the Source form.


Permission for Use and Modification Without Distribution

(1)  You are permitted to use the Standard Version and create and use
Modified Versions for any purpose without restriction, provided that
you do not Distribute the Modified Version.


Permissions for Redistribution of the Standard Version

(2)  You may Distribute verbatim copies of the Source form of the
Standard Version of this Package in any medium without restriction,
either gratis or for a Distributor Fee, provided that you duplicate
all of the original copyright notices and associated disclaimers.  At
your discretion, such verbatim copies may or may not include a
Compiled form of the Package.

(3)  You may apply any bug fixes, portability changes, and other
modifications made available from the Copyright Holder.  The resulting
Package will still be considered the Standard Version, and as such
will be subject to the Original License.


Distribution of Modified Versions of the Package as Source

(4)  You may Distribute your Modified Version as Source (either gratis
or for a Distributor Fee, and with or without a Compiled form of the
Modified Version) provided that you clearly document how it differs
from the Standard Version, including, but not limited to, documenting
any non-standard features, executables, or modules, and provided that
you do at least ONE of the following:

    (a)  make the Modified Version available to the Copyright Holder
    of the Standard Version, under the Original License, so that the
    Copyright Holder may include your modifications in the Standard
    Version.

    (b)  ensure that installation of your Modified Version does not
    prevent the user installing or running the Standard Version. In
    addition, the Modified Version must bear a name that is different
    from the name of the Standard Version.

    (c)  allow anyone who receives a copy of the Modified Version to
    make the Source form of the Modified Version available to others
    under

	(i)  the Original License or

	(ii)  a license that permits the licensee to freely copy,
	modify and redistribute the Modified Version using the same
	licensing terms that apply to the copy that the licensee
	received, and requires that the Source form of the Modified
	Version, and of any works derived from it, be made freely
	available in that license fees are prohibited but Distributor
	Fees are allowed.


Distribution of Compiled Forms of the Standard Version
or Modified Versions without the Source

(5)  You may Distribute Compiled forms of the Standard Version without
the Source, provided that you include complete instructions on how to
get the Source of the Standard Version.  Such instructions must be
valid at the time of your distribution.  If these instructions, at any
time while you are carrying out such distribution, become invalid, you
must provide new instructions on demand or cease further distribution.
If you provide valid instructions or cease distribution within thirty
days after you become aware that the instructions are invalid, then
you do not forfeit any of your rights under this license.

(6)  You may Distribute a Modified Version in Compiled form without
the Source, provided that you comply with Section 4 with respect to
the Source of the Modified Version.


Aggregating or Linking the Package

(7)  You may aggregate the Package (either the Standard Version or
Modified Version) with other packages and Distribute the resulting
aggregation provided that you do not charge a licensing fee for the
Package.  Distributor Fees are permitted, and licensing fees for other
components in the aggregation are permitted. The terms of this license
apply to the use and Distribution of the Standard or Modified Versions
as included in the aggregation.

(8) You are permitted to link Modified and Standard Versions with
other works, to embed the Package in a larger work of your own, or to
build stand-alone binary or bytecode versions of applications that
include the Package, and Distribute the result without restriction,
provided the result does not expose a direct interface to the Package.


Items That are Not Considered Part of a Modified Version

(9) Works (including, but not limited to, modules and scripts) that
merely extend or make use of the Package, do not, by themselves, cause
the Package to be a Modified Version.  In addition, such works are not
considered parts of the Package itself, and are not subject to the
terms of this license.


General Provisions

(10)  Any use, modification, and distribution of the Standard or
Modified Versions is governed by this Artistic License. By using,
modifying or distributing the Package, you accept this license. Do not
use, modify, or distribute the Package, if you do not accept this
license.

(11)  If your Modified Version has been derived from a Modified
Version made by someone other than you, you are nevertheless required
to ensure that your Modified Version complies with the requirements of
this license.

(12)  This license does not grant you the right to use any trademark,
service mark, tradename, or logo of the Copyright Holder.

(13)  This license includes the non-exclusive, worldwide,
free-of-charge patent license to make, have made, use, offer to sell,
sell, import and otherwise transfer the Package with respect to any
patent claims licensable by the Copyright Holder that are necessarily
infringed by the Package. If you institute patent litigation
(including a cross-claim or counterclaim) against any party alleging
that the Package constitutes direct or contributory patent
infringement, then this Artistic License to you shall terminate on the
date that such litigation is filed.

(14)  Disclaimer of Warranty:
THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/licenses/raw/bsd-2-clause.txt b/licenses/raw/bsd-2-clause.txt
new file mode 100755
index 0000000..50d4613
--- /dev/null
+++ b/licenses/raw/bsd-2-clause.txt
@@ -0,0 +1,25 @@
BSD 2-Clause License

Copyright (c) [year], [fullname]
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/licenses/raw/bsd-3-clause-clear.txt b/licenses/raw/bsd-3-clause-clear.txt
new file mode 100755
index 0000000..25cd06b
--- /dev/null
+++ b/licenses/raw/bsd-3-clause-clear.txt
@@ -0,0 +1,32 @@
The Clear BSD License

Copyright (c) [year] [fullname]
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:

     * Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.

     * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.

     * Neither the name of the copyright holder nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/licenses/raw/bsd-3-clause.txt b/licenses/raw/bsd-3-clause.txt
new file mode 100755
index 0000000..a378b1e
--- /dev/null
+++ b/licenses/raw/bsd-3-clause.txt
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) [year], [fullname]
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder 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 HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/licenses/raw/bsl-1.0.txt b/licenses/raw/bsl-1.0.txt
new file mode 100755
index 0000000..127a5bc
--- /dev/null
+++ b/licenses/raw/bsl-1.0.txt
@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/licenses/raw/cc-by-4.0.txt b/licenses/raw/cc-by-4.0.txt
new file mode 100755
index 0000000..52bd145
--- /dev/null
+++ b/licenses/raw/cc-by-4.0.txt
@@ -0,0 +1,395 @@
Attribution 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
	wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More_considerations
     for the public:
	wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution 4.0 International Public License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Adapter's License means the license You apply to Your Copyright
     and Similar Rights in Your contributions to Adapted Material in
     accordance with the terms and conditions of this Public License.

  c. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  d. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  e. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  f. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  g. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  h. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  i. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  j. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  k. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part; and

            b. produce, reproduce, and Share Adapted Material.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material (including in modified
          form), You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.

       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.

       4. If You Share Adapted Material You produce, the Adapter's
          License You apply must not prevent recipients of the Adapted
          Material from complying with this Public License.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material; and

  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.


=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.
\ No newline at end of file
diff --git a/licenses/raw/cc-by-sa-4.0.txt b/licenses/raw/cc-by-sa-4.0.txt
new file mode 100755
index 0000000..33bec29
--- /dev/null
+++ b/licenses/raw/cc-by-sa-4.0.txt
@@ -0,0 +1,427 @@
Attribution-ShareAlike 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
	wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More_considerations
     for the public:
	wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution-ShareAlike 4.0 International Public
License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Adapter's License means the license You apply to Your Copyright
     and Similar Rights in Your contributions to Adapted Material in
     accordance with the terms and conditions of this Public License.

  c. BY-SA Compatible License means a license listed at
     creativecommons.org/compatiblelicenses, approved by Creative
     Commons as essentially the equivalent of this Public License.

  d. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  e. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  f. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  g. License Elements means the license attributes listed in the name
     of a Creative Commons Public License. The License Elements of this
     Public License are Attribution and ShareAlike.

  h. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  i. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  j. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  k. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  l. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  m. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part; and

            b. produce, reproduce, and Share Adapted Material.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. Additional offer from the Licensor -- Adapted Material.
               Every recipient of Adapted Material from You
               automatically receives an offer from the Licensor to
               exercise the Licensed Rights in the Adapted Material
               under the conditions of the Adapter's License You apply.

            c. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material (including in modified
          form), You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.

       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.

  b. ShareAlike.

     In addition to the conditions in Section 3(a), if You Share
     Adapted Material You produce, the following conditions also apply.

       1. The Adapter's License You apply must be a Creative Commons
          license with the same License Elements, this version or
          later, or a BY-SA Compatible License.

       2. You must include the text of, or the URI or hyperlink to, the
          Adapter's License You apply. You may satisfy this condition
          in any reasonable manner based on the medium, means, and
          context in which You Share Adapted Material.

       3. You may not offer or impose any additional or different terms
          or conditions on, or apply any Effective Technological
          Measures to, Adapted Material that restrict exercise of the
          rights granted under the Adapter's License You apply.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material,

     including for purposes of Section 3(b); and
  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.


=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.
\ No newline at end of file
diff --git a/licenses/raw/cc0-1.0.txt b/licenses/raw/cc0-1.0.txt
new file mode 100755
index 0000000..3bbbc1e
--- /dev/null
+++ b/licenses/raw/cc0-1.0.txt
@@ -0,0 +1,116 @@
CC0 1.0 Universal

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").

Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.

For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:

  i. the right to reproduce, adapt, distribute, perform, display, communicate,
  and translate a Work;

  ii. moral rights retained by the original author(s) and/or performer(s);

  iii. publicity and privacy rights pertaining to a person's image or likeness
  depicted in a Work;

  iv. rights protecting against unfair competition in regards to a Work,
  subject to the limitations in paragraph 4(a), below;

  v. rights protecting the extraction, dissemination, use and reuse of data in
  a Work;

  vi. database rights (such as those arising under Directive 96/9/EC of the
  European Parliament and of the Council of 11 March 1996 on the legal
  protection of databases, and under any national implementation thereof,
  including any amended or successor version of such directive); and

  vii. other similar, equivalent or corresponding rights throughout the world
  based on applicable law or treaty, and any national implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.

4. Limitations and Disclaimers.

  a. No trademark or patent rights held by Affirmer are waived, abandoned,
  surrendered, licensed or otherwise affected by this document.

  b. Affirmer offers the Work as-is and makes no representations or warranties
  of any kind concerning the Work, express, implied, statutory or otherwise,
  including without limitation warranties of title, merchantability, fitness
  for a particular purpose, non infringement, or the absence of latent or
  other defects, accuracy, or the present or absence of errors, whether or not
  discoverable, all to the greatest extent permissible under applicable law.

  c. Affirmer disclaims responsibility for clearing rights of other persons
  that may apply to the Work or any use thereof, including without limitation
  any person's Copyright and Related Rights in the Work. Further, Affirmer
  disclaims responsibility for obtaining any necessary consents, permissions
  or other rights required for any use of the Work.

  d. Affirmer understands and acknowledges that Creative Commons is not a
  party to this document and has no duty or obligation with respect to this
  CC0 or use of the Work.

For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>
\ No newline at end of file
diff --git a/licenses/raw/ecl-2.0.txt b/licenses/raw/ecl-2.0.txt
new file mode 100755
index 0000000..c207709
--- /dev/null
+++ b/licenses/raw/ecl-2.0.txt
@@ -0,0 +1,203 @@
Educational Community License

Version 2.0, April 2007

http://opensource.org/licenses/ECL-2.0

The Educational Community License version 2.0 ("ECL") consists of the Apache
2.0 license, modified to change the scope of the patent grant in section 3 to
be specific to the needs of the education communities using this license. The
original Apache 2.0 license can be found at:
http://www.apache.org/licenses/LICENSE-2.0

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the
copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other
entities that control, are controlled by, or are under common control with
that entity. For the purposes of this definition, "control" means (i) the
power, direct or indirect, to cause the direction or management of such
entity, whether by contract or otherwise, or (ii) ownership of fifty percent
(50%) or more of the outstanding shares, or (iii) beneficial ownership of such
entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.

"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation source, and
configuration files.

"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that is
included in or attached to the work (an example is provided in the Appendix
below).

"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative
Works shall not include works that remain separable from, or merely link (or
bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original
version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the copyright owner or by an individual or Legal
Entity authorized to submit on behalf of the copyright owner. For the purposes
of this definition, "submitted" means any form of electronic, verbal, or
written communication sent to the Licensor or its representatives, including
but not limited to communication on electronic mailing lists, source code
control systems, and issue tracking systems that are managed by, or on behalf
of, the Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise designated
in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.

2. Grant of Copyright License.

Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and
such Derivative Works in Source or Object form.

3. Grant of Patent License.

Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such
Contributor that are necessarily infringed by their Contribution(s) alone or
by combination of their Contribution(s) with the Work to which such
Contribution(s) was submitted. If You institute patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging that
the Work or a Contribution incorporated within the Work constitutes direct or
contributory patent infringement, then any patent licenses granted to You
under this License for that Work shall terminate as of the date such
litigation is filed. Any patent license granted hereby with respect to
contributions by an individual employed by an institution or organization is
limited to patent claims where the individual that is the author of the Work
is also the inventor of the patent claims licensed, and where the organization
or institution has the right to grant such license under applicable grant and
research funding agreements. No other express or implied licenses are granted.

4. Redistribution.

You may reproduce and distribute copies of the Work or Derivative Works
thereof in any medium, with or without modifications, and in Source or Object
form, provided that You meet the following conditions:

You must give any other recipients of the Work or Derivative Works a copy of
this License; and You must cause any modified files to carry prominent notices
stating that You changed the files; and You must retain, in the Source form of
any Derivative Works that You distribute, all copyright, patent, trademark,
and attribution notices from the Source form of the Work, excluding those
notices that do not pertain to any part of the Derivative Works; and If the
Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of
the following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License. You may add Your own copyright statement to Your
modifications and may provide additional or different license terms and
conditions for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use, reproduction, and
distribution of the Work otherwise complies with the conditions stated in this
License.

5. Submission of Contributions.

Unless You explicitly state otherwise, any Contribution intentionally
submitted for inclusion in the Work by You to the Licensor shall be under the
terms and conditions of this License, without any additional terms or
conditions. Notwithstanding the above, nothing herein shall supersede or
modify the terms of any separate license agreement you may have executed with
Licensor regarding such Contributions.

6. Trademarks.

This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.

7. Disclaimer of Warranty.

Unless required by applicable law or agreed to in writing, Licensor provides
the Work (and each Contributor provides its Contributions) on an "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You
are solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.

8. Limitation of Liability.

In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a result of
this License or out of the use or inability to use the Work (including but not
limited to damages for loss of goodwill, work stoppage, computer failure or
malfunction, or any and all other commercial damages or losses), even if such
Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability.

While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License.
However, in accepting such obligations, You may act only on Your own behalf
and on Your sole responsibility, not on behalf of any other Contributor, and
only if You agree to indemnify, defend, and hold each Contributor harmless for
any liability incurred by, or claims asserted against, such Contributor by
reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Educational Community License to your work

To apply the Educational Community License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]" replaced with
your own identifying information. (Don't include the brackets!) The text
should be enclosed in the appropriate comment syntax for the file format. We
also recommend that a file or class name and description of purpose be
included on the same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner] Licensed under the Educational
Community License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

http://opensource.org/licenses/ECL-2.0

 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
\ No newline at end of file
diff --git a/licenses/raw/epl-1.0.txt b/licenses/raw/epl-1.0.txt
new file mode 100755
index 0000000..f6735a1
--- /dev/null
+++ b/licenses/raw/epl-1.0.txt
@@ -0,0 +1,204 @@
Eclipse Public License - v 1.0

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

1. DEFINITIONS

"Contribution" means:
     a) in the case of the initial Contributor, the initial code and
     documentation distributed under this Agreement, and
     b) in the case of each subsequent Contributor:
          i) changes to the Program, and
          ii) additions to the Program;

where such changes and/or additions to the Program originate from and are
distributed by that particular Contributor. A Contribution 'originates' from a
Contributor if it was added to the Program by such Contributor itself or
anyone acting on such Contributor's behalf. Contributions do not include
additions to the Program which: (i) are separate modules of software
distributed in conjunction with the Program under their own license agreement,
and (ii) are not derivative works of the Program.
"Contributor" means any person or entity that distributes the Program.

"Licensed Patents" mean patent claims licensable by a Contributor which are
necessarily infringed by the use or sale of its Contribution alone or when
combined with the Program.

"Program" means the Contributions distributed in accordance with this
Agreement.

"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.

2. GRANT OF RIGHTS

     a) Subject to the terms of this Agreement, each Contributor hereby grants
     Recipient a non-exclusive, worldwide, royalty-free copyright license to
     reproduce, prepare derivative works of, publicly display, publicly
     perform, distribute and sublicense the Contribution of such Contributor,
     if any, and such derivative works, in source code and object code form.

     b) Subject to the terms of this Agreement, each Contributor hereby grants
     Recipient a non-exclusive, worldwide, royalty-free patent license under
     Licensed Patents to make, use, sell, offer to sell, import and otherwise
     transfer the Contribution of such Contributor, if any, in source code and
     object code form. This patent license shall apply to the combination of
     the Contribution and the Program if, at the time the Contribution is
     added by the Contributor, such addition of the Contribution causes such
     combination to be covered by the Licensed Patents. The patent license
     shall not apply to any other combinations which include the Contribution.
     No hardware per se is licensed hereunder.

     c) Recipient understands that although each Contributor grants the
     licenses to its Contributions set forth herein, no assurances are
     provided by any Contributor that the Program does not infringe the patent
     or other intellectual property rights of any other entity. Each
     Contributor disclaims any liability to Recipient for claims brought by
     any other entity based on infringement of intellectual property rights or
     otherwise. As a condition to exercising the rights and licenses granted
     hereunder, each Recipient hereby assumes sole responsibility to secure
     any other intellectual property rights needed, if any. For example, if a
     third party patent license is required to allow Recipient to distribute
     the Program, it is Recipient's responsibility to acquire that license
     before distributing the Program.

     d) Each Contributor represents that to its knowledge it has sufficient
     copyright rights in its Contribution, if any, to grant the copyright
     license set forth in this Agreement.

3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form under
its own license agreement, provided that:

     a) it complies with the terms and conditions of this Agreement; and

     b) its license agreement:
          i) effectively disclaims on behalf of all Contributors all
          warranties and conditions, express and implied, including warranties
          or conditions of title and non-infringement, and implied warranties
          or conditions of merchantability and fitness for a particular
          purpose;
          ii) effectively excludes on behalf of all Contributors all liability
          for damages, including direct, indirect, special, incidental and
          consequential damages, such as lost profits;
          iii) states that any provisions which differ from this Agreement are
          offered by that Contributor alone and not by any other party; and
          iv) states that source code for the Program is available from such
          Contributor, and informs licensees how to obtain it in a reasonable
          manner on or through a medium customarily used for software
          exchange.

When the Program is made available in source code form:

     a) it must be made available under this Agreement; and

     b) a copy of this Agreement must be included with each copy of the
     Program.
Contributors may not remove or alter any copyright notices contained within
the Program.

Each Contributor must identify itself as the originator of its Contribution,
if any, in a manner that reasonably allows subsequent Recipients to identify
the originator of the Contribution.

4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities with
respect to end users, business partners and the like. While this license is
intended to facilitate the commercial use of the Program, the Contributor who
includes the Program in a commercial product offering should do so in a manner
which does not create potential liability for other Contributors. Therefore,
if a Contributor includes the Program in a commercial product offering, such
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
every other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits and
other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such Commercial
Contributor in connection with its distribution of the Program in a commercial
product offering. The obligations in this section do not apply to any claims
or Losses relating to any actual or alleged intellectual property
infringement. In order to qualify, an Indemnified Contributor must: a)
promptly notify the Commercial Contributor in writing of such claim, and b)
allow the Commercial Contributor to control, and cooperate with the Commercial
Contributor in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own expense.

For example, a Contributor might include the Program in a commercial product
offering, Product X. That Contributor is then a Commercial Contributor. If
that Commercial Contributor then makes performance claims, or offers
warranties related to Product X, those performance claims and warranties are
such Commercial Contributor's responsibility alone. Under this section, the
Commercial Contributor would have to defend claims against the other
Contributors related to those performance claims and warranties, and if a
court requires any other Contributor to pay any damages as a result, the
Commercial Contributor must pay those damages.

5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
Recipient is solely responsible for determining the appropriateness of using
and distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to the
risks and costs of program errors, compliance with applicable laws, damage to
or loss of data, programs or equipment, and unavailability or interruption of
operations.

6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGES.

7. GENERAL

If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of the
remainder of the terms of this Agreement, and without further action by the
parties hereto, such provision shall be reformed to the minimum extent
necessary to make such provision valid and enforceable.

If Recipient institutes patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
(excluding combinations of the Program with other software or hardware)
infringes such Recipient's patent(s), then such Recipient's rights granted
under Section 2(b) shall terminate as of the date such litigation is filed.

All Recipient's rights under this Agreement shall terminate if it fails to
comply with any of the material terms or conditions of this Agreement and does
not cure such failure in a reasonable period of time after becoming aware of
such noncompliance. If all Recipient's rights under this Agreement terminate,
Recipient agrees to cease use and distribution of the Program as soon as
reasonably practicable. However, Recipient's obligations under this Agreement
and any licenses granted by Recipient relating to the Program shall continue
and survive.

Everyone is permitted to copy and distribute copies of this Agreement, but in
order to avoid inconsistency the Agreement is copyrighted and may only be
modified in the following manner. The Agreement Steward reserves the right to
publish new versions (including revisions) of this Agreement from time to
time. No one other than the Agreement Steward has the right to modify this
Agreement. The Eclipse Foundation is the initial Agreement Steward. The
Eclipse Foundation may assign the responsibility to serve as the Agreement
Steward to a suitable separate entity. Each new version of the Agreement will
be given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version of the
Agreement is published, Contributor may elect to distribute the Program
(including its Contributions) under the new version. Except as expressly
stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
licenses to the intellectual property of any Contributor under this Agreement,
whether expressly, by implication, estoppel or otherwise. All rights in the
Program not expressly granted under this Agreement are reserved.

This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to this
Agreement will bring a legal action under this Agreement more than one year
after the cause of action arose. Each party waives its rights to a jury trial
in any resulting litigation.
\ No newline at end of file
diff --git a/licenses/raw/epl-2.0.txt b/licenses/raw/epl-2.0.txt
new file mode 100755
index 0000000..e23ece2
--- /dev/null
+++ b/licenses/raw/epl-2.0.txt
@@ -0,0 +1,277 @@
Eclipse Public License - v 2.0

    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
    PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

1. DEFINITIONS

"Contribution" means:

  a) in the case of the initial Contributor, the initial content
     Distributed under this Agreement, and

  b) in the case of each subsequent Contributor:
     i) changes to the Program, and
     ii) additions to the Program;
  where such changes and/or additions to the Program originate from
  and are Distributed by that particular Contributor. A Contribution
  "originates" from a Contributor if it was added to the Program by
  such Contributor itself or anyone acting on such Contributor's behalf.
  Contributions do not include changes or additions to the Program that
  are not Modified Works.

"Contributor" means any person or entity that Distributes the Program.

"Licensed Patents" mean patent claims licensable by a Contributor which
are necessarily infringed by the use or sale of its Contribution alone
or when combined with the Program.

"Program" means the Contributions Distributed in accordance with this
Agreement.

"Recipient" means anyone who receives the Program under this Agreement
or any Secondary License (as applicable), including Contributors.

"Derivative Works" shall mean any work, whether in Source Code or other
form, that is based on (or derived from) the Program and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship.

"Modified Works" shall mean any work in Source Code or other form that
results from an addition to, deletion from, or modification of the
contents of the Program, including, for purposes of clarity any new file
in Source Code form that contains any contents of the Program. Modified
Works shall not include works that contain only declarations,
interfaces, types, classes, structures, or files of the Program solely
in each case in order to link to, bind by name, or subclass the Program
or Modified Works thereof.

"Distribute" means the acts of a) distributing or b) making available
in any manner that enables the transfer of a copy.

"Source Code" means the form of a Program preferred for making
modifications, including but not limited to software source code,
documentation source, and configuration files.

"Secondary License" means either the GNU General Public License,
Version 2.0, or any later versions of that license, including any
exceptions or additional permissions as identified by the initial
Contributor.

2. GRANT OF RIGHTS

  a) Subject to the terms of this Agreement, each Contributor hereby
  grants Recipient a non-exclusive, worldwide, royalty-free copyright
  license to reproduce, prepare Derivative Works of, publicly display,
  publicly perform, Distribute and sublicense the Contribution of such
  Contributor, if any, and such Derivative Works.

  b) Subject to the terms of this Agreement, each Contributor hereby
  grants Recipient a non-exclusive, worldwide, royalty-free patent
  license under Licensed Patents to make, use, sell, offer to sell,
  import and otherwise transfer the Contribution of such Contributor,
  if any, in Source Code or other form. This patent license shall
  apply to the combination of the Contribution and the Program if, at
  the time the Contribution is added by the Contributor, such addition
  of the Contribution causes such combination to be covered by the
  Licensed Patents. The patent license shall not apply to any other
  combinations which include the Contribution. No hardware per se is
  licensed hereunder.

  c) Recipient understands that although each Contributor grants the
  licenses to its Contributions set forth herein, no assurances are
  provided by any Contributor that the Program does not infringe the
  patent or other intellectual property rights of any other entity.
  Each Contributor disclaims any liability to Recipient for claims
  brought by any other entity based on infringement of intellectual
  property rights or otherwise. As a condition to exercising the
  rights and licenses granted hereunder, each Recipient hereby
  assumes sole responsibility to secure any other intellectual
  property rights needed, if any. For example, if a third party
  patent license is required to allow Recipient to Distribute the
  Program, it is Recipient's responsibility to acquire that license
  before distributing the Program.

  d) Each Contributor represents that to its knowledge it has
  sufficient copyright rights in its Contribution, if any, to grant
  the copyright license set forth in this Agreement.

  e) Notwithstanding the terms of any Secondary License, no
  Contributor makes additional grants to any Recipient (other than
  those set forth in this Agreement) as a result of such Recipient's
  receipt of the Program under the terms of a Secondary License
  (if permitted under the terms of Section 3).

3. REQUIREMENTS

3.1 If a Contributor Distributes the Program in any form, then:

  a) the Program must also be made available as Source Code, in
  accordance with section 3.2, and the Contributor must accompany
  the Program with a statement that the Source Code for the Program
  is available under this Agreement, and informs Recipients how to
  obtain it in a reasonable manner on or through a medium customarily
  used for software exchange; and

  b) the Contributor may Distribute the Program under a license
  different than this Agreement, provided that such license:
     i) effectively disclaims on behalf of all other Contributors all
     warranties and conditions, express and implied, including
     warranties or conditions of title and non-infringement, and
     implied warranties or conditions of merchantability and fitness
     for a particular purpose;

     ii) effectively excludes on behalf of all other Contributors all
     liability for damages, including direct, indirect, special,
     incidental and consequential damages, such as lost profits;

     iii) does not attempt to limit or alter the recipients' rights
     in the Source Code under section 3.2; and

     iv) requires any subsequent distribution of the Program by any
     party to be under a license that satisfies the requirements
     of this section 3.

3.2 When the Program is Distributed as Source Code:

  a) it must be made available under this Agreement, or if the
  Program (i) is combined with other material in a separate file or
  files made available under a Secondary License, and (ii) the initial
  Contributor attached to the Source Code the notice described in
  Exhibit A of this Agreement, then the Program may be made available
  under the terms of such Secondary Licenses, and

  b) a copy of this Agreement must be included with each copy of
  the Program.

3.3 Contributors may not remove or alter any copyright, patent,
trademark, attribution notices, disclaimers of warranty, or limitations
of liability ("notices") contained within the Program from any copy of
the Program which they Distribute, provided that Contributors may add
their own appropriate notices.

4. COMMERCIAL DISTRIBUTION

Commercial distributors of software may accept certain responsibilities
with respect to end users, business partners and the like. While this
license is intended to facilitate the commercial use of the Program,
the Contributor who includes the Program in a commercial product
offering should do so in a manner which does not create potential
liability for other Contributors. Therefore, if a Contributor includes
the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and indemnify every
other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits
and other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such
Commercial Contributor in connection with its distribution of the Program
in a commercial product offering. The obligations in this section do not
apply to any claims or Losses relating to any actual or alleged
intellectual property infringement. In order to qualify, an Indemnified
Contributor must: a) promptly notify the Commercial Contributor in
writing of such claim, and b) allow the Commercial Contributor to control,
and cooperate with the Commercial Contributor in, the defense and any
related settlement negotiations. The Indemnified Contributor may
participate in any such claim at its own expense.

For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those performance
claims and warranties, and if a court requires any other Contributor to
pay any damages as a result, the Commercial Contributor must pay
those damages.

5. NO WARRANTY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE. Each Recipient is solely responsible for determining the
appropriateness of using and distributing the Program and assumes all
risks associated with its exercise of rights under this Agreement,
including but not limited to the risks and costs of program errors,
compliance with applicable laws, damage to or loss of data, programs
or equipment, and unavailability or interruption of operations.

6. DISCLAIMER OF LIABILITY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

7. GENERAL

If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.

If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other software
or hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.

All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of
time after becoming aware of such noncompliance. If all Recipient's
rights under this Agreement terminate, Recipient agrees to cease use
and distribution of the Program as soon as reasonably practicable.
However, Recipient's obligations under this Agreement and any licenses
granted by Recipient relating to the Program shall continue and survive.

Everyone is permitted to copy and distribute copies of this Agreement,
but in order to avoid inconsistency the Agreement is copyrighted and
may only be modified in the following manner. The Agreement Steward
reserves the right to publish new versions (including revisions) of
this Agreement from time to time. No one other than the Agreement
Steward has the right to modify this Agreement. The Eclipse Foundation
is the initial Agreement Steward. The Eclipse Foundation may assign the
responsibility to serve as the Agreement Steward to a suitable separate
entity. Each new version of the Agreement will be given a distinguishing
version number. The Program (including Contributions) may always be
Distributed subject to the version of the Agreement under which it was
received. In addition, after a new version of the Agreement is published,
Contributor may elect to Distribute the Program (including its
Contributions) under the new version.

Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
receives no rights or licenses to the intellectual property of any
Contributor under this Agreement, whether expressly, by implication,
estoppel or otherwise. All rights in the Program not expressly granted
under this Agreement are reserved. Nothing in this Agreement is intended
to be enforceable by any entity that is not a Contributor or Recipient.
No third-party beneficiary rights are created under this Agreement.

Exhibit A - Form of Secondary Licenses Notice

"This Source Code may also be made available under the following 
Secondary Licenses when the conditions for such availability set forth 
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
version(s), and exceptions or additional permissions here}."

  Simply including a copy of this Agreement, including this Exhibit A
  is not sufficient to license the Source Code under Secondary Licenses.

  If it is not possible or desirable to put the notice in a particular
  file, then You may include the notice in a location (such as a LICENSE
  file in a relevant directory) where a recipient would be likely to
  look for such a notice.

  You may add additional accurate notices of copyright ownership.
\ No newline at end of file
diff --git a/licenses/raw/eupl-1.1.txt b/licenses/raw/eupl-1.1.txt
new file mode 100755
index 0000000..5ae2fc4
--- /dev/null
+++ b/licenses/raw/eupl-1.1.txt
@@ -0,0 +1,311 @@
European Union Public Licence
V. 1.1


EUPL © the European Community 2007


This European Union Public Licence (the “EUPL”) applies to the
Work or Software (as defined below) which is provided under the terms of this
Licence. Any use of the Work, other than as authorised under this Licence is
prohibited (to the extent such use is covered by a right of the copyright
holder of the Work).

The Original Work is provided under the terms of this
Licence when the Licensor (as defined below) has placed the following notice
immediately following the copyright notice for the Original Work:

Licensed under the EUPL V.1.1

or has expressed by any other mean his willingness to license under the EUPL.


1. Definitions

In this Licence, the
following terms have the following meaning:

- The Licence: this Licence.

- The Original Work or the Software: the software distributed
and/or communicated by the Licensor under this Licence, available as Source
Code and also as Executable Code as the case may be.

- Derivative Works:
the works or software that could be created by the Licensee, based upon the
Original Work or modifications thereof. This Licence does not define the
extent of modification or dependence on the Original Work required in order to
classify a work as a Derivative Work; this extent is determined by copyright
law applicable in the country mentioned in Article 15.

- The Work: the Original Work and/or its Derivative Works.

- The Source Code: the human-readable form of the Work which is the most
convenient for people to study and modify.

- The Executable Code: any code which has generally been compiled and which
is meant to be interpreted by a computer as a program.

- The Licensor: the natural or legal person that distributes and/or
communicates the Work under the Licence.

- Contributor(s): any natural or legal person who modifies the Work under the
Licence, or otherwise contributes to the creation of a Derivative Work.

- The Licensee or “You”: any natural or legal person who makes any usage of
the Software under the terms of the Licence.

- Distribution and/or Communication: any act of selling, giving, lending,
renting, distributing, communicating, transmitting, or otherwise
making available, on-line or off-line, copies of the Work or providing access
to its essential functionalities at the disposal of any other natural or legal
person.


2. Scope of the rights granted by the Licence

The Licensor hereby grants You a world-wide, royalty-free, non-exclusive,
sub-licensable licence to do the following, for the duration of copyright
vested in the Original Work:

- use the Work in any circumstance and for all usage,
- reproduce the Work,
- modify the Original Work, and make Derivative Works
based upon the Work,
- communicate to the public, including the right to make available or display
the Work or copies thereof to the public and perform publicly, as the case
may be, the Work,
- distribute the Work or copies thereof,
- lend and rent the Work or copies thereof,
- sub-license rights in the Work or copies thereof.

Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.

In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make
effective the licence of the economic rights here above listed.

The Licensor grants to the Licensee royalty-free, non exclusive usage rights
to any patents held by the Licensor, to the extent necessary to make use of
the rights granted on the Work under this Licence.


3. Communication of the Source Code

The Licensor may provide the Work either
in its Source Code form, or as Executable Code. If the Work is provided as
Executable Code, the Licensor provides in addition a machine-readable copy of
the Source Code of the Work along with each copy of the Work that the Licensor
distributes or indicates, in a notice following the copyright notice attached
to the Work, a repository where the Source Code is easily and freely
accessible for as long as the Licensor continues to distribute and/or
communicate the Work.


4. Limitations on copyright

Nothing in this Licence is intended to deprive the Licensee of the benefits
from any exception or limitation to the exclusive rights of the rights owners
in the Original Work or Software, of the exhaustion of those rights or of
other applicable limitations thereto.


5. Obligations of the Licensee

The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:

Attribution right:
the Licensee shall keep intact all copyright, patent or trademarks notices and
all notices that refer to the Licence and to the disclaimer of warranties. The
Licensee must include a copy of such notices and a copy of the Licence with
every copy of the Work he/she distributes and/or communicates. The Licensee
must cause any Derivative Work to carry prominent notices stating that the
Work has been modified and the date of modification.

Copyleft clause:
If the Licensee distributes and/or communicates copies of the Original Works
or Derivative Works based upon the Original Work, this Distribution and/or
Communication will be done under the terms of this Licence or of a later
version of this Licence unless the Original Work is expressly distributed only
under this version of the Licence. The Licensee (becoming Licensor) cannot
offer or impose any additional terms or conditions on the Work or Derivative
Work that alter or restrict the terms of the Licence.

Compatibility clause:
If the Licensee Distributes and/or Communicates Derivative Works or copies
thereof based upon both the Original Work and another work  licensed under a
Compatible Licence, this Distribution and/or Communication can be done under
the terms of this Compatible Licence. For the sake of this clause,
“Compatible Licence” refers to the licences listed in the appendix
attached to this Licence. Should the Licensee’s obligations under the
Compatible Licence conflict with his/her obligations under this Licence, the
obligations of the Compatible Licence shall prevail.

Provision of Source Code:
When distributing and/or communicating copies of the Work, the Licensee
will provide a machine-readable copy of the Source Code or indicate a
repository where this Source will be easily and freely available for as long
as the Licensee continues to distribute and/or communicate the Work.

Legal Protection:
This Licence does not grant permission to use the trade names,
trademarks, service marks, or names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.


6. Chain of Authorship

The original Licensor warrants that the copyright in the Original Work
granted hereunder is owned by him/her or licensed to him/her and
that he/she has the power and authority to grant the Licence.

Each Contributor warrants that the copyright in the modifications he/she
brings to the Work are owned by him/her or licensed to him/her and that
he/she has the power and authority to grant the Licence.

Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under
the terms of this Licence.


7. Disclaimer of Warranty

The Work is a work in progress, which is continuously improved by numerous
contributors. It is not a finished work and may therefore contain defects or
“bugs” inherent to this type of software development.

For the above reason, the Work is provided under the Licence on an “as is”
basis and without warranties of any kind concerning the Work, including
without limitation merchantability, fitness for a particular purpose, absence
of defects or errors, accuracy, non-infringement of intellectual property
rights other than copyright as stated in Article 6 of this Licence.

This disclaimer of warranty is an essential part of the Licence and a
condition for the grant of any rights to the Work.


8. Disclaimer of Liability

Except in the cases of wilful misconduct or damages directly caused to
natural persons, the Licensor will in no event be liable for any direct or
indirect, material or moral, damages of any kind, arising out of the Licence
or of the use of the Work, including without limitation,
damages for loss of goodwill, work stoppage, computer failure or malfunction,
loss of data or any commercial damage, even if the Licensor has been advised
of the possibility of such damage. However, the Licensor will be liable under
statutory product liability laws as far such laws apply to the Work.


9. Additional agreements

While distributing the Original Work or Derivative Works, You may choose
to conclude an additional agreement to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability
obligations and/or services consistent with this Licence. However, in
accepting such obligations, You may act only on your own behalf and on your
sole responsibility, not on behalf of the original Licensor or any other
Contributor, and only if You agree to indemnify, defend, and hold each
Contributor harmless for any liability incurred by, or claims asserted against
such Contributor by the fact You have accepted any such warranty or additional
liability.


10. Acceptance of the Licence

The provisions of this Licence can be accepted by clicking on
an icon “I agree” placed under the bottom of a window displaying the text of
this Licence or by affirming consent in any other similar way, in accordance
with the rules of applicable law. Clicking on that icon indicates your clear
and irrevocable acceptance of this Licence and
all of its terms and conditions.

Similarly, you irrevocably accept this Licence and
all of its terms and conditions by exercising any rights granted to You
by Article 2 of this Licence, such as the use of the Work,
the creation by You of a Derivative Work or the Distribution and/or
Communication by You of the Work or copies thereof.


11. Information to the public

In case of any Distribution and/or Communication of the Work by means of
electronic communication by You (for example, by offering to download
the Work from a remote location) the distribution channel or media (for
example, a website) must at least provide to the public the information
requested by the applicable law regarding the Licensor, the Licence and the
way it may be accessible, concluded, stored and reproduced by the
Licensee.


12. Termination of the Licence

The Licence and the rights granted hereunder will terminate automatically
upon any breach by the Licensee of the terms of the Licence.

Such a termination will not terminate the licences of any person who has
received the Work from the Licensee under the Licence, provided such persons
remain in full compliance with the Licence.


13. Miscellaneous

Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work licensed hereunder.

If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed and/or reformed so as necessary
to make it valid and enforceable.

The European Commission may publish other linguistic versions and/or new
versions of this Licence, so far this is required and reasonable, without
reducing the scope of the rights granted by the Licence.
New versions of the Licence will be published with a unique version number.

All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version
of their choice.


14. Jurisdiction

Any litigation resulting from the interpretation of this License, arising
between the European Commission, as a Licensor, and any Licensee,
will be subject to the jurisdiction of the Court of Justice of the
European Communities, as laid down in article 238 of the Treaty establishing
the European Community.

Any litigation arising between Parties, other than the European Commission,
and resulting from the interpretation of this License, will be subject to the
exclusive jurisdiction of the competent court where the Licensor resides or
conducts its primary business.


15. Applicable Law

This Licence shall be governed by the law of the European Union country where
the Licensor resides or has his registered office.

This licence shall be governed by the Belgian law if:

- a litigation arises between the European Commission, as a Licensor, and any
Licensee;
- the Licensor, other than the European Commission, has no residence or
registered office inside a European Union country.


===


Appendix


“Compatible Licences” according to article 5 EUPL are:
- GNU General Public License (GNU GPL) v. 2
- Open Software License (OSL) v. 2.1, v. 3.0
- Common Public License v. 1.0
- Eclipse Public License v. 1.0
- Cecill v. 2.0
\ No newline at end of file
diff --git a/licenses/raw/eupl-1.2.txt b/licenses/raw/eupl-1.2.txt
new file mode 100755
index 0000000..9a91ccb
--- /dev/null
+++ b/licenses/raw/eupl-1.2.txt
@@ -0,0 +1,274 @@
European Union Public Licence
V. 1.2

EUPL © the European Union 2007, 2016

This European Union Public Licence (the ‘EUPL’) applies to the Work (as
defined below) which is provided under the terms of this Licence. Any use of
the Work, other than as authorised under this Licence is prohibited (to the
extent such use is covered by a right of the copyright holder of the Work).

The Work is provided under the terms of this Licence when the Licensor (as
defined below) has placed the following notice immediately following the
copyright notice for the Work: “Licensed under the EUPL”, or has expressed by
any other means his willingness to license under the EUPL.

1. Definitions

In this Licence, the following terms have the following meaning:
— ‘The Licence’: this Licence.
— ‘The Original Work’: the work or software distributed or communicated by the
  ‘Licensor under this Licence, available as Source Code and also as
  ‘Executable Code as the case may be.
— ‘Derivative Works’: the works or software that could be created by the
  ‘Licensee, based upon the Original Work or modifications thereof. This
  ‘Licence does not define the extent of modification or dependence on the
  ‘Original Work required in order to classify a work as a Derivative Work;
  ‘this extent is determined by copyright law applicable in the country
  ‘mentioned in Article 15.
— ‘The Work’: the Original Work or its Derivative Works.
— ‘The Source Code’: the human-readable form of the Work which is the most
  convenient for people to study and modify.

— ‘The Executable Code’: any code which has generally been compiled and which
  is meant to be interpreted by a computer as a program.
— ‘The Licensor’: the natural or legal person that distributes or communicates
  the Work under the Licence.
— ‘Contributor(s)’: any natural or legal person who modifies the Work under
  the Licence, or otherwise contributes to the creation of a Derivative Work.
— ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
  the Work under the terms of the Licence.
— ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
  renting, distributing, communicating, transmitting, or otherwise making
  available, online or offline, copies of the Work or providing access to its
  essential functionalities at the disposal of any other natural or legal
  person.

2. Scope of the rights granted by the Licence

The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
sublicensable licence to do the following, for the duration of copyright
vested in the Original Work:

— use the Work in any circumstance and for all usage,
— reproduce the Work,
— modify the Work, and make Derivative Works based upon the Work,
— communicate to the public, including the right to make available or display
  the Work or copies thereof to the public and perform publicly, as the case
  may be, the Work,
— distribute the Work or copies thereof,
— lend and rent the Work or copies thereof,
— sublicense rights in the Work or copies thereof.

Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.

In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make
effective the licence of the economic rights here above listed.

The Licensor grants to the Licensee royalty-free, non-exclusive usage rights
to any patents held by the Licensor, to the extent necessary to make use of
the rights granted on the Work under this Licence.

3. Communication of the Source Code

The Licensor may provide the Work either in its Source Code form, or as
Executable Code. If the Work is provided as Executable Code, the Licensor
provides in addition a machine-readable copy of the Source Code of the Work
along with each copy of the Work that the Licensor distributes or indicates,
in a notice following the copyright notice attached to the Work, a repository
where the Source Code is easily and freely accessible for as long as the
Licensor continues to distribute or communicate the Work.

4. Limitations on copyright

Nothing in this Licence is intended to deprive the Licensee of the benefits
from any exception or limitation to the exclusive rights of the rights owners
in the Work, of the exhaustion of those rights or of other applicable
limitations thereto.

5. Obligations of the Licensee

The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:
 
Attribution right: The Licensee shall keep intact all copyright, patent or
trademarks notices and all notices that refer to the Licence and to the
disclaimer of warranties. The Licensee must include a copy of such notices and
a copy of the Licence with every copy of the Work he/she distributes or
communicates. The Licensee must cause any Derivative Work to carry prominent
notices stating that the Work has been modified and the date of modification.

Copyleft clause: If the Licensee distributes or communicates copies of the
Original Works or Derivative Works, this Distribution or Communication will be
done under the terms of this Licence or of a later version of this Licence
unless the Original Work is expressly distributed only under this version of
the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
(becoming Licensor) cannot offer or impose any additional terms or conditions
on the Work or Derivative Work that alter or restrict the terms of the
Licence.

Compatibility clause: If the Licensee Distributes or Communicates Derivative
Works or copies thereof based upon both the Work and another work licensed
under a Compatible Licence, this Distribution or Communication can be done
under the terms of this Compatible Licence. For the sake of this clause,
‘Compatible Licence’ refers to the licences listed in the appendix attached to
this Licence. Should the Licensee's obligations under the Compatible Licence
conflict with his/her obligations under this Licence, the obligations of the
Compatible Licence shall prevail.

Provision of Source Code: When distributing or communicating copies of the
Work, the Licensee will provide a machine-readable copy of the Source Code or
indicate a repository where this Source will be easily and freely available
for as long as the Licensee continues to distribute or communicate the Work.

Legal Protection: This Licence does not grant permission to use the trade
names, trademarks, service marks, or names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.

6. Chain of Authorship

The original Licensor warrants that the copyright in the Original Work granted
hereunder is owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each Contributor warrants that the copyright in the modifications he/she
brings to the Work are owned by him/her or licensed to him/her and that he/she
has the power and authority to grant the Licence.

Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under the
terms of this Licence.

7. Disclaimer of Warranty

The Work is a work in progress, which is continuously improved by numerous
Contributors. It is not a finished work and may therefore contain defects or
‘bugs’ inherent to this type of development.

For the above reason, the Work is provided under the Licence on an ‘as is’
basis and without warranties of any kind concerning the Work, including
without limitation merchantability, fitness for a particular purpose, absence
of defects or errors, accuracy, non-infringement of intellectual property
rights other than copyright as stated in Article 6 of this Licence.

This disclaimer of warranty is an essential part of the Licence and a
condition for the grant of any rights to the Work.

8. Disclaimer of Liability

Except in the cases of wilful misconduct or damages directly caused to natural
persons, the Licensor will in no event be liable for any direct or indirect,
material or moral, damages of any kind, arising out of the Licence or of the
use of the Work, including without limitation, damages for loss of goodwill,
work stoppage, computer failure or malfunction, loss of data or any commercial
damage, even if the Licensor has been advised of the possibility of such
damage. However, the Licensor will be liable under statutory product liability
laws as far such laws apply to the Work.

9. Additional agreements

While distributing the Work, You may choose to conclude an additional
agreement, defining obligations or services consistent with this Licence.
However, if accepting obligations, You may act only on your own behalf and on
your sole responsibility, not on behalf of the original Licensor or any other
Contributor, and only if You agree to indemnify, defend, and hold each
Contributor harmless for any liability incurred by, or claims asserted against
such Contributor by the fact You have accepted any warranty or additional
liability.

10. Acceptance of the Licence

The provisions of this Licence can be accepted by clicking on an icon ‘I
agree’ placed under the bottom of a window displaying the text of this Licence
or by affirming consent in any other similar way, in accordance with the rules
of applicable law. Clicking on that icon indicates your clear and irrevocable
acceptance of this Licence and all of its terms and conditions.

Similarly, you irrevocably accept this Licence and all of its terms and
conditions by exercising any rights granted to You by Article 2 of this
Licence, such as the use of the Work, the creation by You of a Derivative Work
or the Distribution or Communication by You of the Work or copies thereof.

11. Information to the public

In case of any Distribution or Communication of the Work by means of
electronic communication by You (for example, by offering to download the Work
from a remote location) the distribution channel or media (for example, a
website) must at least provide to the public the information requested by the
applicable law regarding the Licensor, the Licence and the way it may be
accessible, concluded, stored and reproduced by the Licensee.

12. Termination of the Licence

The Licence and the rights granted hereunder will terminate automatically upon
any breach by the Licensee of the terms of the Licence. Such a termination
will not terminate the licences of any person who has received the Work from
the Licensee under the Licence, provided such persons remain in full
compliance with the Licence.

13. Miscellaneous

Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work.

If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed or reformed so as necessary to make it
valid and enforceable.

The European Commission may publish other linguistic versions or new versions
of this Licence or updated versions of the Appendix, so far this is required
and reasonable, without reducing the scope of the rights granted by the
Licence. New versions of the Licence will be published with a unique version
number.

All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version of
their choice.

14. Jurisdiction

Without prejudice to specific agreement between parties,
— any litigation resulting from the interpretation of this License, arising
  between the European Union institutions, bodies, offices or agencies, as a
  Licensor, and any Licensee, will be subject to the jurisdiction of the Court
  of Justice of the European Union, as laid down in article 272 of the Treaty
  on the Functioning of the European Union,
— any litigation arising between other parties and resulting from the
  interpretation of this License, will be subject to the exclusive
  jurisdiction of the competent court where the Licensor resides or conducts
  its primary business.

15. Applicable Law

Without prejudice to specific agreement between parties,
— this Licence shall be governed by the law of the European Union Member State
  where the Licensor has his seat, resides or has his registered office,
— this licence shall be governed by Belgian law if the Licensor has no seat,
  residence or registered office inside a European Union Member State.

Appendix

‘Compatible Licences’ according to Article 5 EUPL are:
— GNU General Public License (GPL) v. 2, v. 3
— GNU Affero General Public License (AGPL) v. 3
— Open Software License (OSL) v. 2.1, v. 3.0
— Eclipse Public License (EPL) v. 1.0
— CeCILL v. 2.0, v. 2.1
— Mozilla Public Licence (MPL) v. 2
— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
  works other than software
— European Union Public Licence (EUPL) v. 1.1, v. 1.2
— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or
  Strong Reciprocity (LiLiQ-R+)

— The European Commission may update this Appendix to later versions of the
  above licences without producing a new version of the EUPL, as long as they
  provide the rights granted in Article 2 of this Licence and protect the
  covered Source Code from exclusive appropriation.
— All other changes or additions to this Appendix require the production of a
  new EUPL version.
\ No newline at end of file
diff --git a/licenses/raw/gpl-2.0.txt b/licenses/raw/gpl-2.0.txt
new file mode 100755
index 0000000..ecbc059
--- /dev/null
+++ b/licenses/raw/gpl-2.0.txt
@@ -0,0 +1,339 @@
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
\ No newline at end of file
diff --git a/licenses/raw/gpl-3.0.txt b/licenses/raw/gpl-3.0.txt
new file mode 100755
index 0000000..20d40b6
--- /dev/null
+++ b/licenses/raw/gpl-3.0.txt
@@ -0,0 +1,674 @@
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
\ No newline at end of file
diff --git a/licenses/raw/isc.txt b/licenses/raw/isc.txt
new file mode 100755
index 0000000..830d75a
--- /dev/null
+++ b/licenses/raw/isc.txt
@@ -0,0 +1,15 @@
ISC License

Copyright (c) [year], [fullname]

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
\ No newline at end of file
diff --git a/licenses/raw/lgpl-2.1.txt b/licenses/raw/lgpl-2.1.txt
new file mode 100755
index 0000000..ee9ed41
--- /dev/null
+++ b/licenses/raw/lgpl-2.1.txt
@@ -0,0 +1,504 @@
                  GNU LESSER GENERAL PUBLIC LICENSE
                       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

                  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

                     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
    USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random
  Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
\ No newline at end of file
diff --git a/licenses/raw/lgpl-3.0.txt b/licenses/raw/lgpl-3.0.txt
new file mode 100755
index 0000000..02bbb60
--- /dev/null
+++ b/licenses/raw/lgpl-3.0.txt
@@ -0,0 +1,165 @@
                   GNU LESSER GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.


  This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.

  0. Additional Definitions.

  As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.

  "The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.

  An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.

  A "Combined Work" is a work produced by combining or linking an
Application with the Library.  The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".

  The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.

  The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.

  1. Exception to Section 3 of the GNU GPL.

  You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.

  2. Conveying Modified Versions.

  If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:

   a) under this License, provided that you make a good faith effort to
   ensure that, in the event an Application does not supply the
   function or data, the facility still operates, and performs
   whatever part of its purpose remains meaningful, or

   b) under the GNU GPL, with none of the additional permissions of
   this License applicable to that copy.

  3. Object Code Incorporating Material from Library Header Files.

  The object code form of an Application may incorporate material from
a header file that is part of the Library.  You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:

   a) Give prominent notice with each copy of the object code that the
   Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the object code with a copy of the GNU GPL and this license
   document.

  4. Combined Works.

  You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:

   a) Give prominent notice with each copy of the Combined Work that
   the Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the Combined Work with a copy of the GNU GPL and this license
   document.

   c) For a Combined Work that displays copyright notices during
   execution, include the copyright notice for the Library among
   these notices, as well as a reference directing the user to the
   copies of the GNU GPL and this license document.

   d) Do one of the following:

       0) Convey the Minimal Corresponding Source under the terms of this
       License, and the Corresponding Application Code in a form
       suitable for, and under terms that permit, the user to
       recombine or relink the Application with a modified version of
       the Linked Version to produce a modified Combined Work, in the
       manner specified by section 6 of the GNU GPL for conveying
       Corresponding Source.

       1) Use a suitable shared library mechanism for linking with the
       Library.  A suitable mechanism is one that (a) uses at run time
       a copy of the Library already present on the user's computer
       system, and (b) will operate properly with a modified version
       of the Library that is interface-compatible with the Linked
       Version.

   e) Provide Installation Information, but only if you would otherwise
   be required to provide such information under section 6 of the
   GNU GPL, and only to the extent that such information is
   necessary to install and execute a modified version of the
   Combined Work produced by recombining or relinking the
   Application with a modified version of the Linked Version. (If
   you use option 4d0, the Installation Information must accompany
   the Minimal Corresponding Source and Corresponding Application
   Code. If you use option 4d1, you must provide the Installation
   Information in the manner specified by section 6 of the GNU GPL
   for conveying Corresponding Source.)

  5. Combined Libraries.

  You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:

   a) Accompany the combined library with a copy of the same work based
   on the Library, uncombined with any other library facilities,
   conveyed under the terms of this License.

   b) Give prominent notice with the combined library that part of it
   is a work based on the Library, and explaining where to find the
   accompanying uncombined form of the same work.

  6. Revised Versions of the GNU Lesser General Public License.

  The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.

  Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.

  If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
\ No newline at end of file
diff --git a/licenses/raw/lppl-1.3c.txt b/licenses/raw/lppl-1.3c.txt
new file mode 100755
index 0000000..35620fd
--- /dev/null
+++ b/licenses/raw/lppl-1.3c.txt
@@ -0,0 +1,415 @@
The LaTeX Project Public License
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

LPPL Version 1.3c  2008-05-04

Copyright 1999 2002-2008 LaTeX3 Project
    Everyone is allowed to distribute verbatim copies of this
    license document, but modification of it is not allowed.


PREAMBLE
========

The LaTeX Project Public License (LPPL) is the primary license under
which the LaTeX kernel and the base LaTeX packages are distributed.

You may use this license for any work of which you hold the copyright
and which you wish to distribute.  This license may be particularly
suitable if your work is TeX-related (such as a LaTeX package), but
it is written in such a way that you can use it even if your work is
unrelated to TeX.

The section `WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE',
below, gives instructions, examples, and recommendations for authors
who are considering distributing their works under this license.

This license gives conditions under which a work may be distributed
and modified, as well as conditions under which modified versions of
that work may be distributed.

We, the LaTeX3 Project, believe that the conditions below give you
the freedom to make and distribute modified versions of your work
that conform with whatever technical specifications you wish while
maintaining the availability, integrity, and reliability of
that work.  If you do not see how to achieve your goal while
meeting these conditions, then read the document `cfgguide.tex'
and `modguide.tex' in the base LaTeX distribution for suggestions.


DEFINITIONS
===========

In this license document the following terms are used:

   `Work'
    Any work being distributed under this License.

   `Derived Work'
    Any work that under any applicable law is derived from the Work.

   `Modification'
    Any procedure that produces a Derived Work under any applicable
    law -- for example, the production of a file containing an
    original file associated with the Work or a significant portion of
    such a file, either verbatim or with modifications and/or
    translated into another language.

   `Modify'
    To apply any procedure that produces a Derived Work under any
    applicable law.

   `Distribution'
    Making copies of the Work available from one person to another, in
    whole or in part.  Distribution includes (but is not limited to)
    making any electronic components of the Work accessible by
    file transfer protocols such as FTP or HTTP or by shared file
    systems such as Sun's Network File System (NFS).

   `Compiled Work'
    A version of the Work that has been processed into a form where it
    is directly usable on a computer system.  This processing may
    include using installation facilities provided by the Work,
    transformations of the Work, copying of components of the Work, or
    other activities.  Note that modification of any installation
    facilities provided by the Work constitutes modification of the Work.

   `Current Maintainer'
    A person or persons nominated as such within the Work.  If there is
    no such explicit nomination then it is the `Copyright Holder' under
    any applicable law.

   `Base Interpreter'
    A program or process that is normally needed for running or
    interpreting a part or the whole of the Work.

    A Base Interpreter may depend on external components but these
    are not considered part of the Base Interpreter provided that each
    external component clearly identifies itself whenever it is used
    interactively.  Unless explicitly specified when applying the
    license to the Work, the only applicable Base Interpreter is a
    `LaTeX-Format' or in the case of files belonging to the
    `LaTeX-format' a program implementing the `TeX language'.



CONDITIONS ON DISTRIBUTION AND MODIFICATION
===========================================

1.  Activities other than distribution and/or modification of the Work
are not covered by this license; they are outside its scope.  In
particular, the act of running the Work is not restricted and no
requirements are made concerning any offers of support for the Work.

2.  You may distribute a complete, unmodified copy of the Work as you
received it.  Distribution of only part of the Work is considered
modification of the Work, and no right to distribute such a Derived
Work may be assumed under the terms of this clause.

3.  You may distribute a Compiled Work that has been generated from a
complete, unmodified copy of the Work as distributed under Clause 2
above, as long as that Compiled Work is distributed in such a way that
the recipients may install the Compiled Work on their system exactly
as it would have been installed if they generated a Compiled Work
directly from the Work.

4.  If you are the Current Maintainer of the Work, you may, without
restriction, modify the Work, thus creating a Derived Work.  You may
also distribute the Derived Work without restriction, including
Compiled Works generated from the Derived Work.  Derived Works
distributed in this manner by the Current Maintainer are considered to
be updated versions of the Work.

5.  If you are not the Current Maintainer of the Work, you may modify
your copy of the Work, thus creating a Derived Work based on the Work,
and compile this Derived Work, thus creating a Compiled Work based on
the Derived Work.

6.  If you are not the Current Maintainer of the Work, you may
distribute a Derived Work provided the following conditions are met
for every component of the Work unless that component clearly states
in the copyright notice that it is exempt from that condition.  Only
the Current Maintainer is allowed to add such statements of exemption
to a component of the Work.

  a. If a component of this Derived Work can be a direct replacement
     for a component of the Work when that component is used with the
     Base Interpreter, then, wherever this component of the Work
     identifies itself to the user when used interactively with that
     Base Interpreter, the replacement component of this Derived Work
     clearly and unambiguously identifies itself as a modified version
     of this component to the user when used interactively with that
     Base Interpreter.

  b. Every component of the Derived Work contains prominent notices
     detailing the nature of the changes to that component, or a
     prominent reference to another file that is distributed as part
     of the Derived Work and that contains a complete and accurate log
     of the changes.

  c. No information in the Derived Work implies that any persons,
     including (but not limited to) the authors of the original version
     of the Work, provide any support, including (but not limited to)
     the reporting and handling of errors, to recipients of the
     Derived Work unless those persons have stated explicitly that
     they do provide such support for the Derived Work.

  d. You distribute at least one of the following with the Derived Work:

       1. A complete, unmodified copy of the Work;
          if your distribution of a modified component is made by
          offering access to copy the modified component from a
          designated place, then offering equivalent access to copy
          the Work from the same or some similar place meets this
          condition, even though third parties are not compelled to
          copy the Work along with the modified component;

       2. Information that is sufficient to obtain a complete,
          unmodified copy of the Work.

7.  If you are not the Current Maintainer of the Work, you may
distribute a Compiled Work generated from a Derived Work, as long as
the Derived Work is distributed to all recipients of the Compiled
Work, and as long as the conditions of Clause 6, above, are met with
regard to the Derived Work.

8.  The conditions above are not intended to prohibit, and hence do not
apply to, the modification, by any method, of any component so that it
becomes identical to an updated version of that component of the Work as
it is distributed by the Current Maintainer under Clause 4, above.

9.  Distribution of the Work or any Derived Work in an alternative
format, where the Work or that Derived Work (in whole or in part) is
then produced by applying some process to that format, does not relax or
nullify any sections of this license as they pertain to the results of
applying that process.

10. a. A Derived Work may be distributed under a different license
       provided that license itself honors the conditions listed in
       Clause 6 above, in regard to the Work, though it does not have
       to honor the rest of the conditions in this license.

    b. If a Derived Work is distributed under a different license, that
       Derived Work must provide sufficient documentation as part of
       itself to allow each recipient of that Derived Work to honor the
       restrictions in Clause 6 above, concerning changes from the Work.

11. This license places no restrictions on works that are unrelated to
the Work, nor does this license place any restrictions on aggregating
such works with the Work by any means.

12.  Nothing in this license is intended to, or may be used to, prevent
complete compliance by all parties with all applicable laws.


NO WARRANTY
===========

There is no warranty for the Work.  Except when otherwise stated in
writing, the Copyright Holder provides the Work `as is', without
warranty of any kind, either expressed or implied, including, but not
limited to, the implied warranties of merchantability and fitness for a
particular purpose.  The entire risk as to the quality and performance
of the Work is with you.  Should the Work prove defective, you assume
the cost of all necessary servicing, repair, or correction.

In no event unless required by applicable law or agreed to in writing
will The Copyright Holder, or any author named in the components of the
Work, or any other party who may distribute and/or modify the Work as
permitted above, be liable to you for damages, including any general,
special, incidental or consequential damages arising out of any use of
the Work or out of inability to use the Work (including, but not limited
to, loss of data, data being rendered inaccurate, or losses sustained by
anyone as a result of any failure of the Work to operate with any other
programs), even if the Copyright Holder or said author or said other
party has been advised of the possibility of such damages.


MAINTENANCE OF THE WORK
=======================

The Work has the status `author-maintained' if the Copyright Holder
explicitly and prominently states near the primary copyright notice in
the Work that the Work can only be maintained by the Copyright Holder
or simply that it is `author-maintained'.

The Work has the status `maintained' if there is a Current Maintainer
who has indicated in the Work that they are willing to receive error
reports for the Work (for example, by supplying a valid e-mail
address). It is not required for the Current Maintainer to acknowledge
or act upon these error reports.

The Work changes from status `maintained' to `unmaintained' if there
is no Current Maintainer, or the person stated to be Current
Maintainer of the work cannot be reached through the indicated means
of communication for a period of six months, and there are no other
significant signs of active maintenance.

You can become the Current Maintainer of the Work by agreement with
any existing Current Maintainer to take over this role.

If the Work is unmaintained, you can become the Current Maintainer of
the Work through the following steps:

 1.  Make a reasonable attempt to trace the Current Maintainer (and
     the Copyright Holder, if the two differ) through the means of
     an Internet or similar search.

 2.  If this search is successful, then enquire whether the Work
     is still maintained.

  a. If it is being maintained, then ask the Current Maintainer
     to update their communication data within one month.

  b. If the search is unsuccessful or no action to resume active
     maintenance is taken by the Current Maintainer, then announce
     within the pertinent community your intention to take over
     maintenance.  (If the Work is a LaTeX work, this could be
     done, for example, by posting to comp.text.tex.)

 3a. If the Current Maintainer is reachable and agrees to pass
     maintenance of the Work to you, then this takes effect
     immediately upon announcement.

  b. If the Current Maintainer is not reachable and the Copyright
     Holder agrees that maintenance of the Work be passed to you,
     then this takes effect immediately upon announcement.

 4.  If you make an `intention announcement' as described in 2b. above
     and after three months your intention is challenged neither by
     the Current Maintainer nor by the Copyright Holder nor by other
     people, then you may arrange for the Work to be changed so as
     to name you as the (new) Current Maintainer.

 5.  If the previously unreachable Current Maintainer becomes
     reachable once more within three months of a change completed
     under the terms of 3b) or 4), then that Current Maintainer must
     become or remain the Current Maintainer upon request provided
     they then update their communication data within one month.

A change in the Current Maintainer does not, of itself, alter the fact
that the Work is distributed under the LPPL license.

If you become the Current Maintainer of the Work, you should
immediately provide, within the Work, a prominent and unambiguous
statement of your status as Current Maintainer.  You should also
announce your new status to the same pertinent community as
in 2b) above.


WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE
======================================================

This section contains important instructions, examples, and
recommendations for authors who are considering distributing their
works under this license.  These authors are addressed as `you' in
this section.

Choosing This License or Another License
----------------------------------------

If for any part of your work you want or need to use *distribution*
conditions that differ significantly from those in this license, then
do not refer to this license anywhere in your work but, instead,
distribute your work under a different license.  You may use the text
of this license as a model for your own license, but your license
should not refer to the LPPL or otherwise give the impression that
your work is distributed under the LPPL.

The document `modguide.tex' in the base LaTeX distribution explains
the motivation behind the conditions of this license.  It explains,
for example, why distributing LaTeX under the GNU General Public
License (GPL) was considered inappropriate.  Even if your work is
unrelated to LaTeX, the discussion in `modguide.tex' may still be
relevant, and authors intending to distribute their works under any
license are encouraged to read it.

A Recommendation on Modification Without Distribution
-----------------------------------------------------

It is wise never to modify a component of the Work, even for your own
personal use, without also meeting the above conditions for
distributing the modified component.  While you might intend that such
modifications will never be distributed, often this will happen by
accident -- you may forget that you have modified that component; or
it may not occur to you when allowing others to access the modified
version that you are thus distributing it and violating the conditions
of this license in ways that could have legal implications and, worse,
cause problems for the community.  It is therefore usually in your
best interest to keep your copy of the Work identical with the public
one.  Many works provide ways to control the behavior of that work
without altering any of its licensed components.

How to Use This License
-----------------------

To use this license, place in each of the components of your work both
an explicit copyright notice including your name and the year the work
was authored and/or last substantially modified.  Include also a
statement that the distribution and/or modification of that
component is constrained by the conditions in this license.

Here is an example of such a notice and statement:

  %% pig.dtx
  %% Copyright 2005 M. Y. Name
  %
  % This work may be distributed and/or modified under the
  % conditions of the LaTeX Project Public License, either version 1.3
  % of this license or (at your option) any later version.
  % The latest version of this license is in
  %   http://www.latex-project.org/lppl.txt
  % and version 1.3 or later is part of all distributions of LaTeX
  % version 2005/12/01 or later.
  %
  % This work has the LPPL maintenance status `maintained'.
  %
  % The Current Maintainer of this work is M. Y. Name.
  %
  % This work consists of the files pig.dtx and pig.ins
  % and the derived file pig.sty.

Given such a notice and statement in a file, the conditions
given in this license document would apply, with the `Work' referring
to the three files `pig.dtx', `pig.ins', and `pig.sty' (the last being
generated from `pig.dtx' using `pig.ins'), the `Base Interpreter'
referring to any `LaTeX-Format', and both `Copyright Holder' and
`Current Maintainer' referring to the person `M. Y. Name'.

If you do not want the Maintenance section of LPPL to apply to your
Work, change `maintained' above into `author-maintained'.
However, we recommend that you use `maintained', as the Maintenance
section was added in order to ensure that your Work remains useful to
the community even when you can no longer maintain and support it
yourself.

Derived Works That Are Not Replacements
---------------------------------------

Several clauses of the LPPL specify means to provide reliability and
stability for the user community. They therefore concern themselves
with the case that a Derived Work is intended to be used as a
(compatible or incompatible) replacement of the original Work. If
this is not the case (e.g., if a few lines of code are reused for a
completely different task), then clauses 6b and 6d shall not apply.


Important Recommendations
-------------------------

 Defining What Constitutes the Work

   The LPPL requires that distributions of the Work contain all the
   files of the Work.  It is therefore important that you provide a
   way for the licensee to determine which files constitute the Work.
   This could, for example, be achieved by explicitly listing all the
   files of the Work near the copyright notice of each file or by
   using a line such as:

    % This work consists of all files listed in manifest.txt.

   in that place.  In the absence of an unequivocal list it might be
   impossible for the licensee to determine what is considered by you
   to comprise the Work and, in such a case, the licensee would be
   entitled to make reasonable conjectures as to which files comprise
   the Work.
\ No newline at end of file
diff --git a/licenses/raw/mit.txt b/licenses/raw/mit.txt
new file mode 100755
index 0000000..63b4b68
--- /dev/null
+++ b/licenses/raw/mit.txt
@@ -0,0 +1,21 @@
MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
diff --git a/licenses/raw/mpl-2.0.txt b/licenses/raw/mpl-2.0.txt
new file mode 100755
index 0000000..fa0086a
--- /dev/null
+++ b/licenses/raw/mpl-2.0.txt
@@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================

1. Definitions
--------------

1.1. "Contributor"
    means each individual or legal entity that creates, contributes to
    the creation of, or owns Covered Software.

1.2. "Contributor Version"
    means the combination of the Contributions of others (if any) used
    by a Contributor and that particular Contributor's Contribution.

1.3. "Contribution"
    means Covered Software of a particular Contributor.

1.4. "Covered Software"
    means Source Code Form to which the initial Contributor has attached
    the notice in Exhibit A, the Executable Form of such Source Code
    Form, and Modifications of such Source Code Form, in each case
    including portions thereof.

1.5. "Incompatible With Secondary Licenses"
    means

    (a) that the initial Contributor has attached the notice described
        in Exhibit B to the Covered Software; or

    (b) that the Covered Software was made available under the terms of
        version 1.1 or earlier of the License, but not also under the
        terms of a Secondary License.

1.6. "Executable Form"
    means any form of the work other than Source Code Form.

1.7. "Larger Work"
    means a work that combines Covered Software with other material, in
    a separate file or files, that is not Covered Software.

1.8. "License"
    means this document.

1.9. "Licensable"
    means having the right to grant, to the maximum extent possible,
    whether at the time of the initial grant or subsequently, any and
    all of the rights conveyed by this License.

1.10. "Modifications"
    means any of the following:

    (a) any file in Source Code Form that results from an addition to,
        deletion from, or modification of the contents of Covered
        Software; or

    (b) any new file in Source Code Form that contains any Covered
        Software.

1.11. "Patent Claims" of a Contributor
    means any patent claim(s), including without limitation, method,
    process, and apparatus claims, in any patent Licensable by such
    Contributor that would be infringed, but for the grant of the
    License, by the making, using, selling, offering for sale, having
    made, import, or transfer of either its Contributions or its
    Contributor Version.

1.12. "Secondary License"
    means either the GNU General Public License, Version 2.0, the GNU
    Lesser General Public License, Version 2.1, the GNU Affero General
    Public License, Version 3.0, or any later versions of those
    licenses.

1.13. "Source Code Form"
    means the form of the work preferred for making modifications.

1.14. "You" (or "Your")
    means an individual or a legal entity exercising rights under this
    License. For legal entities, "You" includes any entity that
    controls, is controlled by, or is under common control with You. For
    purposes of this definition, "control" means (a) the power, direct
    or indirect, to cause the direction or management of such entity,
    whether by contract or otherwise, or (b) ownership of more than
    fifty percent (50%) of the outstanding shares or beneficial
    ownership of such entity.

2. License Grants and Conditions
--------------------------------

2.1. Grants

Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:

(a) under intellectual property rights (other than patent or trademark)
    Licensable by such Contributor to use, reproduce, make available,
    modify, display, perform, distribute, and otherwise exploit its
    Contributions, either on an unmodified basis, with Modifications, or
    as part of a Larger Work; and

(b) under Patent Claims of such Contributor to make, use, sell, offer
    for sale, have made, import, and otherwise transfer either its
    Contributions or its Contributor Version.

2.2. Effective Date

The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.

2.3. Limitations on Grant Scope

The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:

(a) for any code that a Contributor has removed from Covered Software;
    or

(b) for infringements caused by: (i) Your and any other third party's
    modifications of Covered Software, or (ii) the combination of its
    Contributions with other software (except as part of its Contributor
    Version); or

(c) under Patent Claims infringed by Covered Software in the absence of
    its Contributions.

This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).

2.4. Subsequent Licenses

No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).

2.5. Representation

Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.

2.7. Conditions

Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.

3. Responsibilities
-------------------

3.1. Distribution of Source Form

All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.

3.2. Distribution of Executable Form

If You distribute Covered Software in Executable Form then:

(a) such Covered Software must also be made available in Source Code
    Form, as described in Section 3.1, and You must inform recipients of
    the Executable Form how they can obtain a copy of such Source Code
    Form by reasonable means in a timely manner, at a charge no more
    than the cost of distribution to the recipient; and

(b) You may distribute such Executable Form under the terms of this
    License, or sublicense it under different terms, provided that the
    license for the Executable Form does not attempt to limit or alter
    the recipients' rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).

3.4. Notices

You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.

4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------

If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.

5. Termination
--------------

5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.

5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.

************************************************************************
*                                                                      *
*  6. Disclaimer of Warranty                                           *
*  -------------------------                                           *
*                                                                      *
*  Covered Software is provided under this License on an "as is"       *
*  basis, without warranty of any kind, either expressed, implied, or  *
*  statutory, including, without limitation, warranties that the       *
*  Covered Software is free of defects, merchantable, fit for a        *
*  particular purpose or non-infringing. The entire risk as to the     *
*  quality and performance of the Covered Software is with You.        *
*  Should any Covered Software prove defective in any respect, You     *
*  (not any Contributor) assume the cost of any necessary servicing,   *
*  repair, or correction. This disclaimer of warranty constitutes an   *
*  essential part of this License. No use of any Covered Software is   *
*  authorized under this License except under this disclaimer.         *
*                                                                      *
************************************************************************

************************************************************************
*                                                                      *
*  7. Limitation of Liability                                          *
*  --------------------------                                          *
*                                                                      *
*  Under no circumstances and under no legal theory, whether tort      *
*  (including negligence), contract, or otherwise, shall any           *
*  Contributor, or anyone who distributes Covered Software as          *
*  permitted above, be liable to You for any direct, indirect,         *
*  special, incidental, or consequential damages of any character      *
*  including, without limitation, damages for lost profits, loss of    *
*  goodwill, work stoppage, computer failure or malfunction, or any    *
*  and all other commercial damages or losses, even if such party      *
*  shall have been informed of the possibility of such damages. This   *
*  limitation of liability shall not apply to liability for death or   *
*  personal injury resulting from such party's negligence to the       *
*  extent applicable law prohibits such limitation. Some               *
*  jurisdictions do not allow the exclusion or limitation of           *
*  incidental or consequential damages, so this exclusion and          *
*  limitation may not apply to You.                                    *
*                                                                      *
************************************************************************

8. Litigation
-------------

Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.

9. Miscellaneous
----------------

This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.

10. Versions of the License
---------------------------

10.1. New Versions

Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.

10.2. Effect of New Versions

You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.

10.3. Modified Versions

If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses

If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.

Exhibit A - Source Code Form License Notice
-------------------------------------------

  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------

  This Source Code Form is "Incompatible With Secondary Licenses", as
  defined by the Mozilla Public License, v. 2.0.
\ No newline at end of file
diff --git a/licenses/raw/ms-pl.txt b/licenses/raw/ms-pl.txt
new file mode 100755
index 0000000..d189af8
--- /dev/null
+++ b/licenses/raw/ms-pl.txt
@@ -0,0 +1,52 @@
Microsoft Public License (Ms-PL)

This license governs use of the accompanying software. If you use the
software, you accept this license. If you do not accept the license, do not
use the software.

1.  Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution"
have the same meaning here as under U.S. copyright law. A "contribution" is
the original software, or any additions or changes to the software. A
"contributor" is any person that distributes its contribution under this
license. "Licensed patents" are a contributor's patent claims that read
directly on its contribution.

2.  Grant of Rights
     (A) Copyright Grant- Subject to the terms of this license, including the
     license conditions and limitations in section 3, each contributor grants
     you a non-exclusive, worldwide, royalty-free copyright license to
     reproduce its contribution, prepare derivative works of its contribution,
     and distribute its contribution or any derivative works that you create.

     (B) Patent Grant- Subject to the terms of this license, including the
     license conditions and limitations in section 3, each contributor grants
     you a non-exclusive, worldwide, royalty-free license under its licensed
     patents to make, have made, use, sell, offer for sale, import, and/or
     otherwise dispose of its contribution in the software or derivative works
     of the contribution in the software.

3.  Conditions and Limitations
     (A) No Trademark License- This license does not grant you rights to use
     any contributors' name, logo, or trademarks.

     (B) If you bring a patent claim against any contributor over patents that
     you claim are infringed by the software, your patent license from such
     contributor to the software ends automatically.

     (C) If you distribute any portion of the software, you must retain all
     copyright, patent, trademark, and attribution notices that are present in
     the software.

     (D) If you distribute any portion of the software in source code form,
     you may do so only under this license by including a complete copy of
     this license with your distribution. If you distribute any portion of the
     software in compiled or object code form, you may only do so under a
     license that complies with this license.

     (E) The software is licensed "as-is." You bear the risk of using it. The
     contributors give no express warranties, guarantees, or conditions. You
     may have additional consumer rights under your local laws which this
     license cannot change. To the extent permitted under your local laws, the
     contributors exclude the implied warranties of merchantability, fitness
     for a particular purpose and non-infringement.
\ No newline at end of file
diff --git a/licenses/raw/ms-rl.txt b/licenses/raw/ms-rl.txt
new file mode 100755
index 0000000..4793fc8
--- /dev/null
+++ b/licenses/raw/ms-rl.txt
@@ -0,0 +1,64 @@
Microsoft Reciprocal License (Ms-RL)

This license governs use of the accompanying software. If you use the
software, you accept this license. If you do not accept the license, do not
use the software.

1.  Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution"
have the same meaning here as under U.S. copyright law.

A "contribution" is the original software, or any additions or changes to the
software.

A "contributor" is any person that distributes its contribution under this
license.

"Licensed patents" are a contributor's patent claims that read directly on its
contribution.

2.  Grant of Rights
     (A) Copyright Grant- Subject to the terms of this license, including the
     license conditions and limitations in section 3, each contributor grants
     you a non-exclusive, worldwide, royalty-free copyright license to
     reproduce its contribution, prepare derivative works of its contribution,
     and distribute its contribution or any derivative works that you create.

     (B) Patent Grant- Subject to the terms of this license, including the
     license conditions and limitations in section 3, each contributor grants
     you a non-exclusive, worldwide, royalty-free license under its licensed
     patents to make, have made, use, sell, offer for sale, import, and/or
     otherwise dispose of its contribution in the software or derivative works
     of the contribution in the software.

3.  Conditions and Limitations
     (A) Reciprocal Grants- For any file you distribute that contains code
     from the software (in source code or binary format), you must provide
     recipients the source code to that file along with a copy of this
     license, which license will govern that file. You may license other files
     that are entirely your own work and do not contain code from the software
     under any terms you choose.

     (B) No Trademark License- This license does not grant you rights to use
     any contributors' name, logo, or trademarks.

     (C) If you bring a patent claim against any contributor over patents that
     you claim are infringed by the software, your patent license from such
     contributor to the software ends automatically.

     (D) If you distribute any portion of the software, you must retain all
     copyright, patent, trademark, and attribution notices that are present in
     the software.

     (E) If you distribute any portion of the software in source code form,
     you may do so only under this license by including a complete copy of
     this license with your distribution. If you distribute any portion of the
     software in compiled or object code form, you may only do so under a
     license that complies with this license.

     (F) The software is licensed "as-is." You bear the risk of using it. The
     contributors give no express warranties, guarantees, or conditions. You
     may have additional consumer rights under your local laws which this
     license cannot change. To the extent permitted under your local laws, the
     contributors exclude the implied warranties of merchantability, fitness
     for a particular purpose and non-infringement.
\ No newline at end of file
diff --git a/licenses/raw/ncsa.txt b/licenses/raw/ncsa.txt
new file mode 100755
index 0000000..3c56190
--- /dev/null
+++ b/licenses/raw/ncsa.txt
@@ -0,0 +1,34 @@
University of Illinois/NCSA Open Source License 

Copyright (c) [year] [fullname]. All rights reserved. 

Developed by: [project] 
              [fullname] 
              [projecturl]
                  
Permission is hereby granted, free of charge, to any person 
obtaining a copy of this software and associated documentation files 
(the "Software"), to deal with the Software without restriction, 
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, 
and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:

* Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimers.

* Redistributions in binary form must reproduce the above copyright 
  notice, this list of conditions and the following disclaimers in the 
  documentation and/or other materials provided with the distribution.

* Neither the names of [fullname], [project] nor the names of its 
  contributors may be used to endorse or promote products derived from
  this Software without specific prior written permission.
  
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
THE SOFTWARE.
\ No newline at end of file
diff --git a/licenses/raw/ofl-1.1.txt b/licenses/raw/ofl-1.1.txt
new file mode 100755
index 0000000..f6cdae6
--- /dev/null
+++ b/licenses/raw/ofl-1.1.txt
@@ -0,0 +1,92 @@
Copyright (c) [year] [fullname] ([email])

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL

-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION AND CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
diff --git a/licenses/raw/osl-3.0.txt b/licenses/raw/osl-3.0.txt
new file mode 100755
index 0000000..b7f5a7d
--- /dev/null
+++ b/licenses/raw/osl-3.0.txt
@@ -0,0 +1,172 @@
Open Software License ("OSL") v 3.0

This Open Software License (the "License") applies to any original work of
authorship (the "Original Work") whose owner (the "Licensor") has placed the
following licensing notice adjacent to the copyright notice for the Original
Work:

Licensed under the Open Software License version 3.0

1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, for the duration of the copyright, to do
the following:

  a) to reproduce the Original Work in copies, either alone or as part of a
  collective work;

  b) to translate, adapt, alter, transform, modify, or arrange the Original
  Work, thereby creating derivative works ("Derivative Works") based upon the
  Original Work;

  c) to distribute or communicate copies of the Original Work and Derivative
  Works to the public, with the proviso that copies of Original Work or
  Derivative Works that You distribute or communicate shall be licensed under
  this Open Software License;

  d) to perform the Original Work publicly; and

  e) to display the Original Work publicly.

2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, under patent claims owned or controlled
by the Licensor that are embodied in the Original Work as furnished by the
Licensor, for the duration of the patents, to make, use, sell, offer for sale,
have made, and import the Original Work and Derivative Works.

3) Grant of Source Code License. The term "Source Code" means the preferred
form of the Original Work for making modifications to it and all available
documentation describing how to modify the Original Work. Licensor agrees to
provide a machine-readable copy of the Source Code of the Original Work along
with each copy of the Original Work that Licensor distributes. Licensor
reserves the right to satisfy this obligation by placing a machine-readable
copy of the Source Code in an information repository reasonably calculated to
permit inexpensive and convenient access by You for as long as Licensor
continues to distribute the Original Work.

4) Exclusions From License Grant. Neither the names of Licensor, nor the names
of any contributors to the Original Work, nor any of their trademarks or
service marks, may be used to endorse or promote products derived from this
Original Work without express prior permission of the Licensor. Except as
expressly stated herein, nothing in this License grants any license to
Licensor's trademarks, copyrights, patents, trade secrets or any other
intellectual property. No patent license is granted to make, use, sell, offer
for sale, have made, or import embodiments of any patent claims other than the
licensed claims defined in Section 2. No license is granted to the trademarks
of Licensor even if such marks are included in the Original Work. Nothing in
this License shall be interpreted to prohibit Licensor from licensing under
terms different from this License any Original Work that Licensor otherwise
would have a right to license.

5) External Deployment. The term "External Deployment" means the use,
distribution, or communication of the Original Work or Derivative Works in any
way such that the Original Work or Derivative Works may be used by anyone
other than You, whether those works are distributed or communicated to those
persons or made available as an application intended for use over a network.
As an express condition for the grants of license hereunder, You must treat
any External Deployment by You of the Original Work or a Derivative Work as a
distribution under section 1(c).

6) Attribution Rights. You must retain, in the Source Code of any Derivative
Works that You create, all copyright, patent, or trademark notices from the
Source Code of the Original Work, as well as any notices of licensing and any
descriptive text identified therein as an "Attribution Notice." You must cause
the Source Code for any Derivative Works that You create to carry a prominent
Attribution Notice reasonably calculated to inform recipients that You have
modified the Original Work.

7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
the copyright in and to the Original Work and the patent rights granted herein
by Licensor are owned by the Licensor or are sublicensed to You under the
terms of this License with the permission of the contributor(s) of those
copyrights and patent rights. Except as expressly stated in the immediately
preceding sentence, the Original Work is provided under this License on an "AS
IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
limitation, the warranties of non-infringement, merchantability or fitness for
a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK
IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
License. No license to the Original Work is granted by this License except
under this disclaimer.

8) Limitation of Liability. Under no circumstances and under no legal theory,
whether in tort (including negligence), contract, or otherwise, shall the
Licensor be liable to anyone for any indirect, special, incidental, or
consequential damages of any character arising as a result of this License or
the use of the Original Work including, without limitation, damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses. This limitation of liability shall not
apply to the extent applicable law prohibits such limitation.

9) Acceptance and Termination. If, at any time, You expressly assented to this
License, that assent indicates your clear and irrevocable acceptance of this
License and all of its terms and conditions. If You distribute or communicate
copies of the Original Work or a Derivative Work, You must make a reasonable
effort under the circumstances to obtain the express assent of recipients to
the terms of this License. This License conditions your rights to undertake
the activities listed in Section 1, including your right to create Derivative
Works based upon the Original Work, and doing so without honoring these terms
and conditions is prohibited by copyright law and international treaty.
Nothing in this License is intended to affect copyright exceptions and
limitations (including "fair use" or "fair dealing"). This License shall
terminate immediately and You may no longer exercise any of the rights granted
to You by this License upon your failure to honor the conditions in Section
1(c).

10) Termination for Patent Action. This License shall terminate automatically
and You may no longer exercise any of the rights granted to You by this
License as of the date You commence an action, including a cross-claim or
counterclaim, against Licensor or any licensee alleging that the Original Work
infringes a patent. This termination provision shall not apply for an action
alleging patent infringement by combinations of the Original Work with other
software or hardware.

11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
License may be brought only in the courts of a jurisdiction wherein the
Licensor resides or in which Licensor conducts its primary business, and under
the laws of that jurisdiction excluding its conflict-of-law provisions. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded. Any use of the Original
Work outside the scope of this License or after its termination shall be
subject to the requirements and penalties of copyright or patent law in the
appropriate jurisdiction. This section shall survive the termination of this
License.

12) Attorneys' Fees. In any action to enforce the terms of this License or
seeking damages relating thereto, the prevailing party shall be entitled to
recover its costs and expenses, including, without limitation, reasonable
attorneys' fees and costs incurred in connection with such action, including
any appeal of such action. This section shall survive the termination of this
License.

13) Miscellaneous. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent necessary
to make it enforceable.

14) Definition of "You" in This License. "You" throughout this License,
whether in upper or lower case, means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this License.
For legal entities, "You" includes any entity that controls, is controlled by,
or is under common control with you. For purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or (ii) ownership
of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.

15) Right to Use. You may use the Original Work in all ways not otherwise
restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You.

16) Modification of This License. This License is Copyright © 2005 Lawrence
Rosen. Permission is granted to copy, distribute, or communicate this License
without modification. Nothing in this License permits You to modify this
License as applied to the Original Work or to Derivative Works. However, You
may modify the text of this License and copy, distribute or communicate your
modified version (the "Modified License") and apply it to other original works
of authorship subject to the following conditions: (i) You may not indicate in
any way that your Modified License is the "Open Software License" or "OSL" and
you may not use those names in the name of your Modified License; (ii) You
must replace the notice specified in the first paragraph above with the notice
"Licensed under <insert your license name here>" or with a notice of your own
that is not confusingly similar to the notice in this License; and (iii) You
may not claim that your original works are open source software unless your
Modified License has been approved by Open Source Initiative (OSI) and You
comply with its license review and certification process.
\ No newline at end of file
diff --git a/licenses/raw/postgresql.txt b/licenses/raw/postgresql.txt
new file mode 100755
index 0000000..7775dd9
--- /dev/null
+++ b/licenses/raw/postgresql.txt
@@ -0,0 +1,19 @@
PostgreSQL Licence

Copyright (c) [year], [fullname]

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement is
hereby granted, provided that the above copyright notice and this paragraph
and the following two paragraphs appear in all copies.

IN NO EVENT SHALL [fullname] BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF [fullname]
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

[fullname] SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
AND [fullname] HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.
\ No newline at end of file
diff --git a/licenses/raw/unlicense.txt b/licenses/raw/unlicense.txt
new file mode 100755
index 0000000..6bb8a29
--- /dev/null
+++ b/licenses/raw/unlicense.txt
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org>
\ No newline at end of file
diff --git a/licenses/raw/upl-1.0.txt b/licenses/raw/upl-1.0.txt
new file mode 100755
index 0000000..d6f8839
--- /dev/null
+++ b/licenses/raw/upl-1.0.txt
@@ -0,0 +1,35 @@
Copyright (c) [year] [fullname]

The Universal Permissive License (UPL), Version 1.0

Subject to the condition set forth below, permission is hereby granted to any
person obtaining a copy of this software, associate documentation and/or data
(collectively the "Software"), free of charge and under any and all copyright
rights in the Software, and any and all patent rights owned or freely
licensable by each licensor hereunder covering either (i) the unmodified
Software as contributed to or provided by such licensor, or (ii) the Larger
Works (as defined below), to deal in both

(a) the Software, and
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
one is included with the Software (each a “Larger Work” to which the Software
is contributed by such licensors),

without restriction, including without limitation the rights to copy, create
derivative works of, display, perform, and distribute the Software and make,
use, sell, offer for sale, import, export, have made, and have sold the
Software and the Larger Work(s), and to sublicense the foregoing rights on
either these or other terms.

This license is subject to the following condition:
The above copyright notice and either this complete permission notice or at
a minimum a reference to the UPL must be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
diff --git a/licenses/raw/wtfpl.txt b/licenses/raw/wtfpl.txt
new file mode 100755
index 0000000..8cffccc
--- /dev/null
+++ b/licenses/raw/wtfpl.txt
@@ -0,0 +1,13 @@
            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                    Version 2, December 2004

 Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>

 Everyone is permitted to copy and distribute verbatim or modified
 copies of this license document, and changing it is allowed as long
 as the name is changed.

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO.
\ No newline at end of file
diff --git a/licenses/raw/zlib.txt b/licenses/raw/zlib.txt
new file mode 100755
index 0000000..7cc8bc7
--- /dev/null
+++ b/licenses/raw/zlib.txt
@@ -0,0 +1,19 @@
zlib License

(C) [year] [fullname]

This software is provided 'as-is', without any express or implied
warranty.  In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
\ No newline at end of file
diff --git a/vendor/fatfree-3.6/INSTALL.md b/vendor/fatfree-3.6/INSTALL.md
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/fatfree-3.6/INSTALL.md
diff --git a/vendor/fatfree-3.6/composer.json b/vendor/fatfree-3.6/composer.json
new file mode 100755
index 0000000..84c1afc
--- /dev/null
+++ b/vendor/fatfree-3.6/composer.json
@@ -0,0 +1,18 @@
{
	"name": "bcosca/fatfree",
	"description": "A powerful yet easy-to-use PHP micro-framework designed to help you build dynamic and robust Web applications - fast!",
	"homepage": "http://fatfreeframework.com/",
	"license": "GPL-3.0",
	"require": {
		"php": ">=5.4"
	},
	"repositories": [
		{
			"type": "vcs",
			"url": "https://github.com/bcosca/fatfree"
		}
	],
	"autoload": {
		"files": ["lib/base.php"]
	}
}
diff --git a/vendor/fatfree-3.6/config.ini b/vendor/fatfree-3.6/config.ini
new file mode 100755
index 0000000..8911dfc
--- /dev/null
+++ b/vendor/fatfree-3.6/config.ini
@@ -0,0 +1,4 @@
[globals]

DEBUG=3
UI=ui/
diff --git a/vendor/fatfree-3.6/index.php b/vendor/fatfree-3.6/index.php
new file mode 100755
index 0000000..d5f4d69
--- /dev/null
+++ b/vendor/fatfree-3.6/index.php
@@ -0,0 +1,86 @@
<?php

// Kickstart the framework
$f3=require('lib/base.php');

$f3->set('DEBUG',1);
if ((float)PCRE_VERSION<7.9)
	trigger_error('PCRE version is out of date');

// Load configuration
$f3->config('config.ini');

$f3->route('GET /',
	function($f3) {
		$classes=array(
			'Base'=>
				array(
					'hash',
					'json',
					'session',
					'mbstring'
				),
			'Cache'=>
				array(
					'apc',
					'apcu',
					'memcache',
					'memcached',
					'redis',
					'wincache',
					'xcache'
				),
			'DB\SQL'=>
				array(
					'pdo',
					'pdo_dblib',
					'pdo_mssql',
					'pdo_mysql',
					'pdo_odbc',
					'pdo_pgsql',
					'pdo_sqlite',
					'pdo_sqlsrv'
				),
			'DB\Jig'=>
				array('json'),
			'DB\Mongo'=>
				array(
					'json',
					'mongo'
				),
			'Auth'=>
				array('ldap','pdo'),
			'Bcrypt'=>
				array(
					'mcrypt',
					'openssl'
				),
			'Image'=>
				array('gd'),
			'Lexicon'=>
				array('iconv'),
			'SMTP'=>
				array('openssl'),
			'Web'=>
				array('curl','openssl','simplexml'),
			'Web\Geo'=>
				array('geoip','json'),
			'Web\OpenID'=>
				array('json','simplexml'),
			'Web\Pingback'=>
				array('dom','xmlrpc')
		);
		$f3->set('classes',$classes);
		$f3->set('content','welcome.htm');
		echo View::instance()->render('layout.htm');
	}
);

$f3->route('GET /userref',
	function($f3) {
		$f3->set('content','userref.htm');
		echo View::instance()->render('layout.htm');
	}
);

$f3->run();
diff --git a/vendor/fatfree-3.6/lib/CHANGELOG.md b/vendor/fatfree-3.6/lib/CHANGELOG.md
new file mode 100755
index 0000000..04950f1
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/CHANGELOG.md
@@ -0,0 +1,872 @@
CHANGELOG

3.6.4 (19 April 2018)
*	NEW: Added Dependency Injection support with CONTAINER variable [#221]https://github.com/bcosca/fatfree-core/issues/221
*	NEW: configurable LOGGABLE error codes [#1091]https://github.com/bcosca/fatfree/issues/1091#issuecomment-364674701
*	NEW: JAR.lifetime option, [#178]https://github.com/bcosca/fatfree-core/issues/178
*	Template: reduced Prefab calls
*	Template: optimized reflection for better derivative support, [bcosca/fatfree#1088]https://github.com/bcosca/fatfree/issues/1088
*	Template: optimized parsing for template attributes and tokens
*	DB\Mongo: fixed logging with mongodb extention
*	DB\Jig: added lazy-loading [#7e1cd9b9b89]https://github.com/bcosca/fatfree-core/commit/7e1cd9b9b89c4175d0f6b86ced9d9bd49c04ac39
*	DB\Jig\Mapper: Added group feature, bcosca/fatfree#616
*	DB\SQL\Mapper: fix PostgreSQL RETURNING ID when no pkey is available, [bcosca/fatfree#1069]https://github.com/bcosca/fatfree/issues/1069, [#230]https://github.com/bcosca/fatfree-core/issues/230
*	DB\SQL\Mapper: disable order clause auto-quoting when it's already been quoted
*	Web->location: add failsafe for geoip_region_name_by_code() [#GB:Bxyn9xn9AgAJ]https://groups.google.com/d/msg/f3-framework/APau4wnwNzE/Bxyn9xn9AgAJ
*	Web->request: Added proxy support [#e936361b]https://github.com/bcosca/fatfree-core/commit/e936361bc03010c4c7c38a396562e5e96a8a100d
*	Web->mime: Added JFIF format
*	Markdown: handle line breaks in paragraph blocks, [bcosca/fatfree#1100]https://github.com/bcosca/fatfree/issues/1100
*	config: reduced cast calls on parsing config sections
*	Patch empty SERVER_NAME [bcosca/fatfree#1084]https://github.com/bcosca/fatfree/issues/1084
*	Bugfix: unreliable request headers in Web->request() response [bcosca/fatfree#1092]https://github.com/bcosca/fatfree/issues/1092
*	Fixed, View->render: utilizing multiple UI paths, [bcosca/fatfree#1083]https://github.com/bcosca/fatfree/issues/1083
*	Fixed URL parsing with PHP 5.4 [#247]https://github.com/bcosca/fatfree-core/issues/247
*	Fixed PHP 7.2 warnings when session is active prematurely, [#238]https://github.com/bcosca/fatfree-core/issues/238
*	Fixed setcookie $expire variable type [#240]https://github.com/bcosca/fatfree-core/issues/240
*	Fixed expiration time when updating an existing cookie

3.6.3 (31 December 2017)
*	PHP7 fix: remove deprecated (unset) cast
*	Web->request: restricted follow_location to 3XX responses only
*	CLI mode: refactored arguments parsing
*	CLI mode: fixed query string encoding
*	SMTP: Refactor parsing of attachments
*	SMTP: clean-up mail headers for multipart messages, [#1065]https://github.com/bcosca/fatfree/issues/1065
*	config: fixed performance issues on parsing config files
*	config: cast command parameters in config entries to php type & constant, [#1030]https://github.com/bcosca/fatfree/issues/1030
*	config: reduced registry calls
*	config: skip hive escaping when resolving dynamic config vars, [#1030]https://github.com/bcosca/fatfree/issues/1030
*	Bug fix: Incorrect cookie lifetime computation, [#1070]https://github.com/bcosca/fatfree/issues/1070, [#1016]https://github.com/bcosca/fatfree/issues/1016
*	DB\SQL\Mapper: use RETURNING option instead of a sequence query to get lastInsertId in PostgreSQL, [#1069]https://github.com/bcosca/fatfree/issues/1069, [#230]https://github.com/bcosca/fatfree-core/issues/230
*	DB\SQL\Session: check if _agent is too long for SQL based sessions [#236]https://github.com/bcosca/fatfree-core/issues/236
*	DB\SQL\Session: fix Session handler table creation issue on SQL Server, [#899]https://github.com/bcosca/fatfree/issues/899
*	DB\SQL: fix oracle db issue with empty error variable, [#1072]https://github.com/bcosca/fatfree/issues/1072
*	DB\SQL\Mapper: fix sorting issues on SQL Server, [#1052]https://github.com/bcosca/fatfree/issues/1052 [#225]https://github.com/bcosca/fatfree-core/issues/225
*	Prevent directory traversal attacks on filesystem based cache [#1073]https://github.com/bcosca/fatfree/issues/1073
*	Bug fix, Template: PHP constants used in include with attribute, [#983]https://github.com/bcosca/fatfree/issues/983
*	Bug fix, Template: Numeric value in expression alters PHP_EOL context
*	Template: use existing linefeed instead of PHP_EOL, [#1048]https://github.com/bcosca/fatfree/issues/1048
*	Template: make newline interpolation handling configurable [#223]https://github.com/bcosca/fatfree-core/issues/223
*	Template: add beforerender to Preview
*	fix custom FORMATS without modifiers
*	Cache: Refactor Cache->reset for XCache
*	Cache: loosen reset cache key pattern, [#1041]https://github.com/bcosca/fatfree/issues/1041
*	XCache: suffix reset only works if xcache.admin.enable_auth is disabled
*	Added HTTP 103 as recently approved by the IETF
*	LDAP changes to for AD flexibility [#227]https://github.com/bcosca/fatfree-core/issues/227
*	Hide debug trace from ajax errors when DEBUG=0 [#1071]https://github.com/bcosca/fatfree/issues/1071
*	fix View->render using potentially wrong cache entry

3.6.2 (26 June 2017)
*   Return a status code > 0 when dying on error [#220]https://github.com/bcosca/fatfree-core/issues/220
*   fix SMTP line width [#215]https://github.com/bcosca/fatfree-core/issues/215
*   Allow using a custom field for ldap user id checking [#217]https://github.com/bcosca/fatfree-core/issues/217
*   NEW: DB\SQL->exists: generic method to check if SQL table exists
*   Pass handler to route handler and hooks [#1035]https://github.com/bcosca/fatfree/issues/1035
*   pass carriage return of multiline dictionary keys
*   Better Web->slug customization
*   fix incorrect header issue [#211]https://github.com/bcosca/fatfree-core/issues/211
*   fix schema issue on databases with case-sensitive collation, fixes [#209]https://github.com/bcosca/fatfree-core/issues/209
*   Add filter for deriving C-locale equivalent of a number
*   Bug fix: @LANGUAGE remains unchanged after override
*   abort: added Header pre-check
*   Assemble URL after ONREROUTE
*   Add reroute argument to skip script termination
*   Invoke ONREROUTE after headers are sent
*   SQLite switch to backtick as quote
*   Bug fix: Incorrect timing in SQL query logs
*   DB\SQL\Mapper: Cast return value of count to integer
*   Patched $_SERVER['REQUEST_URI'] to ensure it contains a relative URI
*   Tweak debug verbosity
*   fix php carriage return issue in preview->build [#205]https://github.com/bcosca/fatfree-core/pull/205
*   fixed template string resolution [#205]https://github.com/bcosca/fatfree-core/pull/205
*   Fixed unexpected default seed on CACHE set [#1028]https://github.com/bcosca/fatfree/issues/1028
*   DB\SQL\Mapper: Optimized field escaping on options
*   Optimize template conversion to PHP file

3.6.1 (2 April 2017)
*	NEW: Recaptcha plugin [#194]https://github.com/bcosca/fatfree-core/pull/194
*	NEW: MB variable for detecting multibyte support
*	NEW: DB\SQL: Cache parsed schema for the TTL duration
*	NEW: quick erase flag on Jig/Mongo/SQL mappers [#193]https://github.com/bcosca/fatfree-core/pull/193
*	NEW: Allow OPTIONS method to return a response body [#171]https://github.com/bcosca/fatfree-core/pull/171
*	NEW: Add support for Memcached (bcosca/fatfree#997)
*	NEW: Rudimentary preload resource (HTTP2 server) support via template push()
*	NEW: Add support for new MongoDB driver [#177]https://github.com/bcosca/fatfree-core/pull/177
*	Changed: template filter are all lowercase now
*	Changed: Fix template lookup inconsistency: removed base dir from UI on render
*	Changed: count() method now has an options argument [#192]https://github.com/bcosca/fatfree-core/pull/192
*	Changed: SMTP, Spit out error message if any
*	\DB\SQL\Mapper: refactored row count strategy
*	DB\SQL\Mapper: Allow non-scalar values to be assigned as mapper property
*	DB\SQL::PARAM_FLOAT: remove cast to float (#106 and bcosca/fatfree#984) (#191)
*	DB\SQL\mapper->erase: allow empty string
*	DB\SQL\mapper->insert: fields reset after successful INSERT
*	Add option to debounce Cursor->paginate subset [#195]https://github.com/bcosca/fatfree-core/pull/195
*	View: Don't delete sandboxed variables (#198)
*	Preview: Optimize compilation of template expressions
*	Preview: Use shorthand tag for direct rendering
*	Preview->resolve(): new tweak to allow template persistence as option
*	Web: Expose diacritics translation table
*	SMTP: Enable logging of message body only when $log argument is 'verbose'
*	SMTP: Convert headers to camelcase for consistency
*	make cache seed more flexible, #164
*	Improve trace details for DEBUG>2
*	Enable config() to read from an array of input files
*	Improved alias and reroute regex
*	Make camelCase and snakeCase Unicode-aware
*	format: Provision for optional whitespaces
*	Break APCu-BC dependence
*	Old PHP 5.3 cleanup
*	Debug log must include HTTP query
*	Recognize X-Forwarded-Port header (bcosca/fatfree#1002)
*	Avoid use of deprecated mcrypt module
*	Return only the client's IP when using the `X-Forwarded-For` header to deduce an IP address
*	Remove orphan mutex locks on termination (#157)
*	Use 80 as default port number to avoid issues when `$_SERVER['SERVER_PORT']` is not existing
*	fread replaced with readfile() for simple send() usecase
*	Bug fix: request URI with multiple leading slashes, #203
*	Bug fix: Query generates wrong adhoc field value
*	Bug fix: SMTP stream context issue #200
*	Bug fix: child pseudo class selector in minify, bcosca/fatfree#1008
*	Bug fix: "Undefined index: CLI" error (#197)
*	Bug fix: cast Cache-Control expire time to int, bcosca/fatfree#1004
*	Bug fix: Avoid issuance of multiple Content-Type headers for nested templates
*	Bug fix: wildcard token issue with digits (bcosca/fatfree#996)
*	Bug fix: afterupdate ignored when row does not change
*	Bug fix: session handler read() method for PHP7 (need strict string) #184 #185
*	Bug fix: reroute mocking in CLI mode (#183)
*	Bug fix: Reroute authoritative relative references (#181)
*	Bug fix: locales order and charset hyphen
*	Bug fix: base stripped twice in router (#176)

3.6.0 (19 November 2016)
*	NEW: [cli] request type
*	NEW: console-friendly CLI mode
*	NEW: lexicon caching
*	NEW: Silent operator skips startup error check (#125)
*	NEW: DB\SQL->trans()
*	NEW: custom config section parser, i.e. [conf > Foo::bar]
*	NEW: support for cache tags in SQL
*	NEW: custom FORMATS
*	NEW: Mongo mapper fields whitelist
*	NEW: WebSocket server
*	NEW: Base->extend method (#158)
*	NEW: Implement framework variable caching via config, i.e. FOO = "bar" | 3600
*	NEW: Lightweight OAuth2 client
*	NEW: SEED variable, configurable app-specific hashing prefix (#149, bcosca/fatfree#951, bcosca/fatfree#884, bcosca/fatfree#629)
*	NEW: CLI variable
*	NEW: Web->send, specify custom filename (#124)
*	NEW: Web->send, added flushing flag (#131)
*	NEW: Indexed route wildcards, now exposed in PARAMS['*']
*	Changed: PHP 5.4 is now the minimum version requirement
*	Changed: Prevent database wrappers from being cloned
*	Changed: Router works on PATH instead of URI (#126) NB: PARAMS.0 no longer contains the query string
*	Changed: Removed ALIASES autobuilding (#118)
*	Changed: Route wildcards match empty strings (#119)
*	Changed: Disable default debug highlighting, HIGHLIGHT is false now
*	General PHP 5.4 optimizations
*	Optimized config parsing
*	Optimized Base->recursive
*	Optimized header extraction
*	Optimized cache/expire headers
*	Optimized session_start behaviour (bcosca/fatfree#673)
*	Optimized reroute regex
*	Tweaked cookie removal
*	Better route precedence order
*	Performance tweak: reduced cache calls
*	Refactored lexicon (LOCALES) build-up, much faster now
*	Added turkish locale bug workaround
*	Geo->tzinfo Update to UTC
*	Added Xcache reset (bcosca/fatfree#928)
*	Redis cache: allow db name in dsn
*	SMTP: Improve server emulation responses
*	SMTP: Optimize transmission envelope
*	SMTP: Implement mock transmission
*	SMTP: Various bug fixes and feature improvements
*	SMTP: quit on failed authentication
*	Geo->weather: force metric units
*	Base->until: Implement CLI interoperability
*	Base->format: looser plural syntax
*	Base->format: Force decimal as default number format
*	Base->merge: Added $keep flag to save result to the hive key
*	Base->reroute: Allow array as URL argument for aliasing
*	Base->alias: Allow query string (or array) to be appended to alias
*	Permit reroute to named routes with URL query segment
*	Sync COOKIE global on set()
*	Permit non-hive variables to use JS dot notation
*	RFC2616: Use absolute URIs for Location header
*	Matrix->calendar: Check if calendar extension is loaded
*	Markdown: require start of line/whitespace for text processing (#136)
*	DB\[SQL|Jig|Mongo]->log(FALSE) disables logging
*	DB\SQL->exec: Added timestamp toggle to db log
*	DB\SQL->schema: Remove unnecessary line terminators
*	DB\SQL\Mapper: allow array filter with empty string
*	DB\SQL\Mapper: optimized handling for key-less tables
*	DB\SQL\Mapper: added float support (#106)
*	DB\SQL\Session: increased default column sizes (#148, bcosca/fatfree#931, bcosca/fatfree#950)
*	Web: Catch cURL errors
*	Optimize Web->receive (bcosca/fatfree#930)
*	Web->minify: fix arbitrary file download vulnerability
*	Web->request: fix cache control max-age detection (bcosca/fatfree#908)
*	Web->request: Add request headers & error message to return value (bcosca/fatfree#737)
*	Web->request: Refactored response to HTTP request
*	Web->send flush while sending big files
*	Image->rgb: allow hex strings
*	Image->captcha: Check if GD module supports TrueType
*	Image->load: Return FALSE on load failure
*	Image->resize: keep aspect ratio when only width or height was given
*	Updated OpenID lib (bcosca/fatfree#965)
*	Audit->card: add new mastercard "2" BIN range (bcosca/fatfree#954)
*	Deprecated: Bcrypt class
*	Preview->render: optimized detection to remove short open PHP tags and allow xml tags (#133)
*	Display file and line number in exception handler (bcosca/fatfree#967)
*	Added error reporting level to Base->error and ERROR.level (bcosca/fatfree#957)
*	Added optional custom cache instance to Session (#141)
*	CLI-aware mock()
*	XFRAME and PACKAGE can be switched off now (#128)
*	Bug fix: wrong time calculation on memcache reset (#170)
*	Bug fix: encode CLI parameters
*	Bug fix: Close connection on abort explicitly (#162)
*	Bug fix: Image->identicon, Avoid double-size sprite rotation (and possible segfault)
*	Bug fix: Image->render and Image->dump, removed unnecessary 2nd argument (#146)
*	Bug fix: Magic->offsetset, access property as array element (#147)
*	Bug fix: multi-line custom template tag parsing (bcosca/fatfree#935)
*	Bug fix: cache headers on errors (bcosca/fatfree#885)
*	Bug fix: Web, deprecated CURLOPT_SSL_VERIFYHOST in curl
*	Bug fix: Web, Invalid user error constant (bcosca/fatfree#962)
*	Bug fix: Web->request, redirections for domain-less location (#135)
*	Bug fix: DB\SQL\Mapper, reset changed flag after update (#142, #152)
*	Bug fix: DB\SQL\Mapper, fix changed flag when using assignment operator #143 #150 #151
*	Bug fix: DB\SQL\Mapper, revival of the HAVING clause
*	Bug fix: DB\SQL\Mapper, pgsql with non-integer primary keys (bcosca/fatfree#916)
*	Bug fix: DB\SQL\Session, quote table name (bcosca/fatfree#977)
*	Bug fix: snakeCase returns word starting with underscore (bcosca/fatfree#927)
*	Bug fix: mock does not populate PATH variable
*	Bug fix: Geo->weather API key (#129)
*	Bug fix: Incorrect compilation of array element with zero index
*	Bug fix: Compilation of array construct is incorrect
*	Bug fix: Trailing slash redirection on UTF-8 paths (#121)

3.5.1 (31 December 2015)
*	NEW: ttl attribute in <include> template tag
*	NEW: allow anonymous function for template filter
*	NEW: format modifier for international and custom currency symbol
*	NEW: Image->data() returns image resource
*	NEW: extract() get prefixed array keys from an assoc array
*	NEW: Optimized and faster Template parser with full support for HTML5 empty tags
*	NEW: Added support for {@token} encapsulation syntax in routes definition
*	NEW: DB\SQL->exec(), automatically shift to 1-based query arguments
*	NEW: abort() flush output
*	Added referenced value to devoid()
*	Template token filters are now resolved within Preview->token()
*	Web->_curl: restrict redirections to HTTP
*	Web->minify(), skip importing of external files
*	Improved session and error handling in until()
*	Get the error trace array with the new $format parameter
*	Better support for unicode URLs
*	Optimized TZ detection with date_default_timezone_get()
*	format() Provide default decimal places
*	Optimize code: remove redundant TTL checks
*	Optimized timeout handling in Web->request()
*	Improved PHPDoc hints
*	Added missing russian DIACRITICS letters
*	DB\Cursor: allow child implementation of reset()
*	DB\Cursor: Copyfrom now does an internal call to set()
*	DB\SQL: Provide the ability to disable SQL logging
*	DB\SQL: improved query analysis to trigger fetchAll
*	DB\SQL\Mapper: added support for binary table columns
*	SQL,JIG,MONGO,CACHE Session handlers refactored and optimized
*	SMTP Refactoring and optimization
*	Bug fix: SMTP, Align quoted_printable_encode() with SMTP specs (dot-stuffing)
*	Bug fix: SMTP, Send buffered optional headers to output
*	Bug fix: SMTP, Content-Transfer-Encoding for non-TLS connections
*	Bug fix: SMTP, Single attachment error
*	Bug fix: Cursor->load not always mapping to first record
*	Bug fix: dry SQL mapper should not trigger 'load'
*	Bug fix: Code highlighting on empty text
*	Bug fix: Image->resize, round dimensions instead of cast
*	Bug fix: whitespace handling in $f3->compile()
*	Bug fix: TTL of `View` and `Preview` (`Template`)
*	Bug fix: token filter regex
*	Bug fix: Template, empty attributes
*	Bug fix: Preview->build() greedy regex
*	Bug fix: Web->minify() single-line comment on last line
*	Bug fix: Web->request(), follow_location with cURL and open_basedir
*	Bug fix: Web->send() Single quotes around filename not interpreted correctly by some browsers

3.5.0 (2 June 2015)
*	NEW: until() method for long polling
*	NEW: abort() to disconnect HTTP client (and continue execution)
*	NEW: SQL Mapper->required() returns TRUE if field is not nullable
*	NEW: PREMAP variable for allowing prefixes to handlers named after HTTP verbs
*	NEW: [configs] section to allow config includes
*	NEW: Test->passed() returns TRUE if no test failed
*	NEW: SQL mapper changed() function
*	NEW: fatfree-core composer support
*	NEW: constants() method to expose constants
*	NEW: Preview->filter() for configurable token filters
*	NEW: CORS variable for Cross-Origin Resource Sharing support, #731
*	Change in behavior: Switch to htmlspecialchars for escaping
*	Change in behavior: No movement in cursor position after erase(), #797
*	Change in behavior: ERROR.trace is a multiline string now
*	Change in behavior: Strict token recognition in <include> href attribute
*	Router fix: loose method search
*	Better route precedence order, #12
*	Preserve contents of ROUTES, #723
*	Alias: allow array of parameters
*	Improvements on reroute method
*	Fix for custom Jig session files
*	Audit: better mobile detection
*	Audit: add argument to test string as browser agent
*	DB mappers: abort insert/update/erase from hooks, #684
*	DB mappers: Allow array inputs in copyfrom()
*	Cache,SQL,Jig,Mongo Session: custom callback for suspect sessions
*	Fix for unexpected HIVE values when defining an empty HIVE array
*	SQL mapper: check for results from CALL and EXEC queries, #771
*	SQL mapper: consider SQL schema prefix, #820
*	SQL mapper: write to log before execution to
	enable tracking of PDOStatement error
*	Add SQL Mapper->table() to return table name
*	Allow override of the schema in SQL Mapper->schema()
*	Improvement: Keep JIG table as reference, #758
*	Expand regex to include whitespaces in SQL DB dsn, #817
*	View: Removed reserved variables $fw and $implicit
*	Add missing newlines after template expansion
*	Web->receive: fix for complex field names, #806
*	Web: Improvements in socket engine
*	Web: customizable user_agent for all engines, #822
*	SMTP: Provision for Content-ID in attachments
*	Image + minify: allow absolute paths
*	Promote framework error to E_USER_ERROR
*	Geo->weather switch to OpenWeather
*	Expose mask() and grab() methods for routing
*	Expose trace() method to expose the debug backtrace
*	Implement recursion strategy using IteratorAggregate, #714
*	Exempt whitespace between % and succeeding operator from being minified, #773
*	Optimized error detection and ONERROR handler, fatfree-core#18
*	Tweak error log output
*	Optimized If-Modified-Since cache header usage
*	Improved APCu compatibility, #724
*	Bug fix: Web::send fails on filename with spaces, #810
*	Bug fix: overwrite limit in findone()
*	Bug fix: locale-specific edge cases affecting SQL schema, #772
*	Bug fix: Newline stripping in config()
*	Bug fix: bracket delimited identifier for sybase and dblib driver
*	Bug fix: Mongo mapper collection->count driver compatibility
*	Bug fix: SQL Mapper->set() forces adhoc value if already defined
*	Bug fix: Mapper ignores HAVING clause
*	Bug fix: Constructor invocation in call()
*	Bug fix: Wrong element returned by ajax/sync request
*	Bug fix: handling of non-consecutive compound key members
*	Bug fix: Virtual fields not retrieved when group option is present, #757
*	Bug fix: group option generates incorrect SQL query, #757
*	Bug fix: ONERROR does not receive PARAMS on fatal error

3.4.0 (1 January 2015)
*	NEW: [redirects] section
*	NEW: Custom config sections
*	NEW: User-defined AUTOLOAD function
*	NEW: ONREROUTE variable
*	NEW: Provision for in-memory Jig database (#727)
*	Return run() result (#687)
*	Pass result of run() to mock() (#687)
*	Add port suffix to REALM variable
*	New attribute in <include> tag to extend hive
*	Adjust unit tests and clean up templates
*	Expose header-related methods
*	Web->request: allow content array
*	Preserve contents of ROUTES (#723)
*	Smart detection of PHP functions in template expressions
*	Add afterrender() hook to View class
*	Implement ArrayAccess and magic properties on hive
*	Improvement on mocking of superglobals and request body
*	Fix table creation for pgsql handled sessions
*	Add QUERY to hive
*	Exempt E_NOTICE from default error_reporting()
*	Add method to build alias routes from template, fixes #693
*	Fix dangerous caching of cookie values
*	Fix multiple encoding in nested templates
*	Fix node attribute parsing for empty/zero values
*	Apply URL encoding on BASE to emulate v2 behavior (#123)
*	Improve Base->map performance (#595)
*	Add simple backtrace for fatal errors
*	Count Cursor->load() results (#581)
*	Add form field name to Web->receive() callback arguments
*	Fix missing newlines after template expansion
*	Fix overwrite of ENCODING variable
*	limit & offset workaround for SQL Server, fixes #671
*	SQL Mapper->find: GROUP BY SQL compliant statement
*	Bug fix: Missing abstract method fields()
*	Bug fix: Auto escaping does not work with mapper objects (#710)
*	Bug fix: 'with' attribute in <include> tag raise error when no token
	inside
*	View rendering: optional Content-Type header
*	Bug fix: Undefined variable: cache (#705)
*	Bug fix: Routing does not work if project base path includes valid
	special URI character (#704)
*	Bug fix: Template hash collision (#702)
*	Bug fix: Property visibility is incorrect (#697)
*	Bug fix: Missing Allow header on HTTP 405 response
*	Bug fix: Double quotes in lexicon files (#681)
*	Bug fix: Space should not be mandatory in ICU pluralization format string
*	Bug fix: Incorrect log entry when SQL query contains a question mark
*	Bug fix: Error stack trace
*	Bug fix: Cookie expiration (#665)
*	Bug fix: OR operator (||) parsed incorrectly
*	Bug fix: Routing treatment of * wildcard character
*	Bug fix:  Mapper copyfrom() method doesn't allow class/object callbacks
	(#590)
*	Bug fix: exists() creates elements/properties (#591)
*	Bug fix: Wildcard in routing pattern consumes entire query string (#592)
*	Bug fix: Workaround bug in latest MongoDB driver
*	Bug fix: Default error handler silently fails for AJAX request with
	DEBUG>0 (#599)
*	Bug fix: Mocked BODY overwritten (#601)
*	Bug fix: Undefined pkey (#607)

3.3.0 (8 August 2014)
*	NEW: Attribute in <include> tag to extend hive
*	NEW: Image overlay with transparency and alignment control
*	NEW: Allow redirection of specified route patterns to a URL
*	Bug fix: Missing AND operator in SQL Server schema query (Issue #576)
*	Count Cursor->load() results (Feature request #581)
*	Mapper copyfrom() method doesn't allow class/object callbacks (Issue #590)
*	Bug fix: exists() creates elements/properties (Issue #591)
*	Bug fix: Wildcard in routing pattern consumes entire query string
	(Issue #592)
*	Tweak Base->map performance (Issue #595)
*	Bug fix: Default error handler silently fails for AJAX request with
	DEBUG>0 (Issue #599)
*	Bug fix: Mocked BODY overwritten (Issue #601)
*	Bug fix: Undefined pkey (Issue #607)
*	Bug fix: beforeupdate() position (Issue #633)
*	Bug fix: exists() return value for cached keys
*	Bug fix: Missing error code in UNLOAD handler
*	Bug fix: OR operator (||) parsed incorrectly
*	Add input name parameter to custom slug function
*	Apply URL encoding on BASE to emulate v2 behavior (Issue #123)
*	Reduce mapper update() iterations
*	Bug fix: Routing treatment of * wildcard character
*	SQL Mapper->find: GROUP BY SQL compliant statement
*	Work around bug in latest MongoDB driver
*	Work around probable race condition and optimize cache access
*	View rendering: Optional Content-Type header
*	Fix missing newlines after template expansion
*	Add form field name to Web->receive() callback arguments
*	Quick reference: add RAW variable

3.2.2 (19 March 2014)
*	NEW: Locales set automatically (Feature request #522)
*	NEW: Mapper dbtype()
*	NEW: before- and after- triggers for all mappers
*	NEW: Decode HTML5 entities if PHP>5.3 detected (Feature request #552)
*	NEW: Send credentials only if AUTH is present in the SMTP extension
	response (Feature request #545)
*	NEW: BITMASK variable to allow ENT_COMPAT override
*	NEW: Redis support for caching
*	Enable SMTP feature detection
*	Enable extended ICU custom date format (Feature request #555)
*	Enable custom time ICU format
*	Add option to turn off session table creation (Feature request #557)
*	Enhanced template token rendering and custom filters (Feature request
	#550)
*	Avert multiple loads in DB-managed sessions (Feature request #558)
*	Add EXEC to associative fetch
*	Bug fix: Building template tokens breaks on inline OR condition (Issue
	#573)
*	Bug fix: SMTP->send does not use the $log parameter (Issue #571)
*	Bug fix: Allow setting sqlsrv primary keys on insert (Issue #570)
*	Bug fix: Generated query for obtaining table schema in sqlsrv incorrect
	(Bug #565)
*	Bug fix: SQL mapper flag set even when value has not changed (Bug #562)
*	Bug fix: Add XFRAME config option (Feature request #546)
*	Bug fix: Incorrect parsing of comments (Issue #541)
*	Bug fix: Multiple Set-Cookie headers (Issue #533)
*	Bug fix: Mapper is dry after save()
*	Bug fix: Prevent infinite loop when error handler is triggered
	(Issue #361)
*	Bug fix: Mapper tweaks not passing primary keys as arguments
*	Bug fix: Zero indexes in dot-notated arrays fail to compile
*	Bug fix: Prevent GROUP clause double-escaping
*	Bug fix: Regression of zlib compression bug
*	Bug fix: Method copyto() does not include ad hoc fields
*	Check existence of OpenID mode (Issue #529)
*	Generate a 404 when a tokenized class doesn't exist
*	Fix SQLite quotes (Issue #521)
*	Bug fix: BASE is incorrect on Windows

3.2.1 (7 January 2014)
*	NEW: EMOJI variable, UTF->translate(), UTF->emojify(), and UTF->strrev()
*	Allow empty strings in config()
*	Add support for turning off php://input buffering via RAW
	(FALSE by default)
*	Add Cursor->load() and Cursor->find() TTL support
*	Support Web->receive() large file downloads via PUT
*	ONERROR safety check
*	Fix session CSRF cookie detection
*	Framework object now passed to route handler contructors
*	Allow override of DIACRITICS
*	Various code optimizations
*	Support log disabling (Issue #483)
*	Implicit mapper load() on authentication
*	Declare abstract methods for Cursor derivatives
*	Support single-quoted HTML/XML attributes (Feature request #503)
*	Relax property visibility of mappers and derivatives
*	Deprecated: {{~ ~}} instructions and {{* *}} comments; Use {~ ~} and
	{* *} instead
*	Minor fix: Audit->ipv4() return value
*	Bug fix: Backslashes in BASE not converted on Windows
*	Bug fix: UTF->substr() with negative offset and specified length
*	Bug fix: Replace named URL tokens on render()
*	Bug fix: BASE is not empty when run from document root
*	Bug fix: stringify() recursion

3.2.0 (18 December 2013)
*	NEW: Automatic CSRF protection (with IP and User-Agent checks) for
	sessions mapped to SQL-, Jig-, Mongo- and Cache-based backends
*	NEW: Named routes
*	NEW: PATH variable; returns the URL relative to BASE
*	NEW: Image->captcha() color parameters
*	NEW: Ability to access MongoCuror thru the cursor() method
*	NEW: Mapper->fields() method returns array of field names
*	NEW: Mapper onload(), oninsert(), onupdate(), and onerase() event
	listeners/triggers
*	NEW: Preview class (a lightweight template engine)
*	NEW: rel() method derives path from URL relative to BASE; useful for
	rerouting
*	NEW: PREFIX variable for prepending a string to a dictionary term;
	Enable support for prefixed dictionary arrays and .ini files (Feature
	request #440)
*	NEW: Google static map plugin
*	NEW: devoid() method
*	Introduce clean(); similar to scrub(), except that arg is passed by
	value
*	Use $ttl for cookie expiration (Issue #457)
*	Fix needs_rehash() cost comparison
*	Add pass-by-reference argument to exists() so if method returns TRUE,
	a subsequent get() is unnecessary
*	Improve MySQL support
*	Move esc(), raw(), and dupe() to View class where they more
	appropriately belong
*	Allow user-defined fields in SQL mapper constructor (Feature request
	#450)
*	Re-implement the pre-3.0 template resolve() feature
*	Remove redundant instances of session_commit()
*	Add support for input filtering in Mapper->copyfrom()
*	Prevent intrusive behavior of Mapper->copyfrom()
*	Support multiple SQL primary keys
*	Support custom tag attributes/inline tokens defined at runtime
	(Feature request #438)
*	Broader support for HTTP basic auth
*	Prohibit Jig _id clear()
*	Add support for detailed stringify() output
*	Add base directory to UI path as fallback
*	Support Test->expect() chaining
*	Support __tostring() in stringify()
*	Trigger error on invalid CAPTCHA length (Issue #458)
*	Bug fix: exists() pass-by-reference argument returns incorrect value
*	Bug fix: DB Exec does not return affected row if query contains a
	sub-SELECT (Issue #437)
*	Improve seed generator and add code for detecting of acceptable
	limits in Image->captcha() (Feature request #460)
*	Add decimal format ICU extension
*	Bug fix: 404-reported URI contains HTTP query
*	Bug fix: Data type detection in DB->schema()
*	Bug fix: TZ initialization
*	Bug fix: paginate() passes incorrect argument to count()
*	Bug fix: Incorrect query when reloading after insert()
*	Bug fix: SQL preg_match error in pdo_type matching (Issue #447)
*	Bug fix: Missing merge() function (Issue #444)
*	Bug fix: BASE misdefined in command line mode
*	Bug fix: Stringifying hive may run infinite (Issue #436)
*	Bug fix: Incomplete stringify() when DEBUG<3 (Issue #432)
*	Bug fix: Redirection of basic auth (Issue #430)
*	Bug fix: Filter only PHP code (including short tags) in templates
*	Bug fix: Markdown paragraph parser does not convert PHP code blocks
	properly
*	Bug fix: identicon() colors on same keys are randomized
*	Bug fix: quotekey() fails on aliased keys
*	Bug fix: Missing _id in Jig->find() return value
*	Bug fix: LANGUAGE/LOCALES handling
*	Bug fix: Loose comparison in stringify()

3.1.2 (5 November 2013)
*	Abandon .chm help format; Package API documentation in plain HTML;
	(Launch lib/api/index.html in your browser)
*	Deprecate BAIL in favor of HALT (default: TRUE)
*	Revert to 3.1.0 autoload behavior; Add support for lowercase folder
	names
*	Allow Spring-style HTTP method overrides
*	Add support for SQL Server-based sessions
*	Capture full X-Forwarded-For header
*	Add protection against malicious scripts; Extra check if file was really
	uploaded
*	Pass-thru page limit in return value of Cursor->paginate()
*	Optimize code: Implement single-pass escaping
*	Short circuit Jig->find() if source file is empty
*	Bug fix: PHP globals passed by reference in hive() result (Issue #424)
*	Bug fix: ZIP mime type incorrect behavior
*	Bug fix: Jig->erase() filter malfunction
*	Bug fix: Mongo->select() group
*	Bug fix: Unknown bcrypt constant

3.1.1 (13 October 2013)
*	NEW: Support OpenID attribute exchange
*	NEW: BAIL variable enables/disables continuance of execution on non-fatal
	errors
*	Deprecate BAIL in favor of HALT (default: FALSE)
*	Add support for Oracle
*	Mark cached queries in log (Feature Request #405)
*	Implement Bcrypt->needs_reshash()
*	Add entropy to SQL cache hash; Add uuid() method to DB backends
*	Find real document root; Simplify debug paths
*	Permit OpenID required fields to be declared as comma-separated string or
	array
*	Pass modified filename as argument to user-defined function in
	Web->receive()
*	Quote keys in optional SQL clauses (Issue #408)
*	Allow UNLOAD to override fatal error detection (Issue #404)
*	Mutex operator precedence error (Issue #406)
*	Bug fix: exists() malfunction (Issue #401)
*	Bug fix: Jig mapper triggers error when loading from CACHE (Issue #403)
*	Bug fix: Array index check
*	Bug fix: OpenID verified() return value
*	Bug fix: Basket->find() should return a set of results (Issue #407);
	Also implemented findone() for consistency with mappers
*	Bug fix: PostgreSQL last insert ID (Issue #410)
*	Bug fix: $port component URL overwritten by _socket()
*	Bug fix: Calculation of elapsed time

3.1.0 (20 August 2013)
*	NEW: Web->filler() returns a chunk of text from the standard
	Lorem Ipsum passage
*	Change in behavior: Drop support for JSON serialization
*	SQL->exec() now returns value of RETURNING clause
*	Add support for $ttl argument in count() (Issue #393)
*	Allow UI to be overridden by custom $path
*	Return result of PDO primitives: begintransaction(), rollback(), and
	commit()
*	Full support for PHP 5.5
*	Flush buffers only when DEBUG=0
*	Support class->method, class::method, and lambda functions as
	Web->basic() arguments
*	Commit session on Basket->save()
*	Optional enlargement in Image->resize()
*	Support authentication on hosts running PHP-CGI
*	Change visibility level of Cache properties
*	Prevent ONERROR recursion
*	Work around Apache pre-2.4 VirtualDocumentRoot bug
*	Prioritize cURL in HTTP engine detection
*	Bug fix: Minify tricky JS
*	Bug fix: desktop() detection
*	Bug fix: Double-slash on TEMP-relative path
*	Bug fix: Cursor mapping of first() and last() records
*	Bug fix: Premature end of Web->receive() on multiple files
*	Bug fix: German umlaute to its corresponding grammatically-correct
	equivalent

3.0.9 (12 June 2013)
*	NEW: Web->whois()
*	NEW: Template <switch> <case> tags
*	Improve CACHE consistency
*	Case-insensitive MIME type detection
*	Support pre-PHP 5.3.4 in Prefab->instance()
*	Refactor isdesktop() and ismobile(); Add isbot()
*	Add support for Markdown strike-through
*	Work around ODBC's lack of quote() support
*	Remove useless Prefab destructor
*	Support multiple cache instances
*	Bug fix: Underscores in OpenId keys mangled
*	Refactor format()
*	Numerous tweaks
*	Bug fix: MongoId object not preserved
*	Bug fix: Double-quotes included in lexicon() string (Issue #341)
*	Bug fix: UTF-8 formatting mangled on Windows (Issue #342)
*	Bug fix: Cache->load() error when CACHE is FALSE (Issue #344)
*	Bug fix: send() ternary expression
*	Bug fix: Country code constants

3.0.8 (17 May 2013)
*	NEW: Bcrypt lightweight hashing library\
*	Return total number of records in superset in Cursor->paginate()
*	ONERROR short-circuit (Enhancement #334)
*	Apply quotes/backticks on DB identifiers
*	Allow enabling/disabling of SQL log
*	Normalize glob() behavior (Issue #330)
*	Bug fix: mbstring 2-byte text truncation (Issue #325)
*	Bug fix: Unsupported operand types (Issue #324)

3.0.7 (2 May 2013)
*	NEW: route() now allows an array of routing patterns as first argument;
	support array as first argument of map()
*	NEW: entropy() for calculating password strength (NIST 800-63)
*	NEW: AGENT variable containing auto-detected HTTP user agent string
*	NEW: ismobile() and isdesktop() methods
*	NEW: Prefab class and descendants now accept constructor arguments
*	Change in behavior: Cache->exists() now returns timestamp and TTL of
	cache entry or FALSE if not found (Feature request #315)
*	Preserve timestamp and TTL when updating cache entry (Feature request
	#316)
*	Improved currency formatting with C99 compliance
*	Suppress unnecessary program halt at startup caused by misconfigured
	server
*	Add support for dashes in custom attribute names in templates
*	Bug fix: Routing precedene (Issue #313)
*	Bug fix: Remove Jig _id element from document property
*	Bug fix: Web->rss() error when not enough items in the feed (Issue #299)
*	Bug fix: Web engine fallback (Issue #300)
*	Bug fix: <strong> and <em> formatting
*	Bug fix: Text rendering of text with trailing punctuation (Issue #303)
*	Bug fix: Incorrect regex in SMTP

3.0.6 (31 Mar 2013)
*	NEW: Image->crop()
*	Modify documentation blocks for PHPDoc interoperability
*	Allow user to control whether Base->rerouet() uses a permanent or
	temporary redirect
*	Allow JAR elements to be set individually
*	Refactor DB\SQL\Mapper->insert() to cope with autoincrement fields
*	Trigger error when captcha() font is missing
*	Remove unnecessary markdown regex recursion
*	Check for scalars instead of DB\SQL strings
*	Implement more comprehensive diacritics table
*	Add option for disabling 401 errors when basic auth() fails
*	Add markdown syntax highlighting for Apache configuration
*	Markdown->render() deprecated to remove dependency on UI variable;
	Feature replaced by Markdown->convert() to enable translation from
	markdown string to HTML
*	Optimize factory() code of all data mappers
*	Apply backticks on MySQL table names
*	Bug fix: Routing failure when directory path contains a tilde (Issue #291)
*	Bug fix: Incorrect markdown parsing of strong/em sequences and inline HTML
*	Bug fix: Cached page not echoed (Issue #278)
*	Bug fix: Object properties not escaped when rendering
*	Bug fix: OpenID error response ignored
*	Bug fix: memcache_get_extended_stats() timeout
*	Bug fix: Base->set() doesn't pass TTL to Cache->set()
*	Bug fix: Base->scrub() ignores pass-thru * argument (Issue #274)

3.0.5 (16 Feb 2013)
*	NEW: Markdown class with PHP, HTML, and .ini syntax highlighting support
*	NEW: Options for caching of select() and find() results
*	NEW: Web->acceptable()
*	Add send() argument for forcing downloads
*	Provide read() option for applying Unix LF as standard line ending
*	Bypass lexicon() call if LANGUAGE is undefined
*	Load fallback language dictionary if LANGUAGE is undefined
*	map() now checks existence of class/methods for non-tokenized URLs
*	Improve error reporting of non-existent Template methods
*	Address output buffer issues on some servers
*	Bug fix: Setting DEBUG to 0 won't suppress the stack trace when the
	content type is application/json (Issue #257)
*	Bug fix: Image dump/render additional arguments shifted
*	Bug fix: ob_clean() causes buffer issues with zlib compression
*	Bug fix: minify() fails when commenting CSS @ rules (Issue #251)
*	Bug fix: Handling of commas inside quoted strings
*	Bug fix: Glitch in stringify() handling of closures
*	Bug fix: dry() in mappers returns TRUE despite being hydrated by
	factory() (Issue #265)
*	Bug fix: expect() not handling flags correctly
*	Bug fix: weather() fails when server is unreachable

3.0.4 (29 Jan 2013)
*	NEW: Support for ICU/CLDR pluralization
*	NEW: User-defined FALLBACK language
*	NEW: minify() now recognizes CSS @import directives
*	NEW: UTF->bom() returns byte order mark for UTF-8 encoding
*	Expose SQL\Mapper->schema()
*	Change in behavior: Send error response as JSON string if AJAX request is
	detected
*	Deprecated: afind*() methods
*	Discard output buffer in favor of debug output
*	Make _id available to Jig queries
*	Magic class now implements ArrayAccess
*	Abort execution on startup errors
*	Suppress stack trace on DEBUG level 0
*	Allow single = as equality operator in Jig query expressions
*	Abort OpenID discovery if Web->request() fails
*	Mimic PHP *RECURSION* in stringify()
*	Modify Jig parser to allow wildcard-search using preg_match()
*	Abort execution after error() execution
*	Concatenate cached/uncached minify() iterations; Prevent spillover
	caching of previous minify() result
*	Work around obscure PHP session id regeneration bug
*	Revise algorithm for Jig filter involving undefined fields (Issue #230)
*	Use checkdnsrr() instead of gethostbyname() in DNSBL check
*	Auto-adjust pagination to cursor boundaries
*	Add Romanian diacritics
*	Bug fix: Root namespace reference and sorting with undefined Jig fields
*	Bug fix: Greedy receive() regex
*	Bug fix: Default LANGUAGE always 'en'
*	Bug fix: minify() hammers cache backend
*	Bug fix: Previous values of primary keys not saved during factory()
	instantiation
*	Bug fix: Jig find() fails when search key is not present in all records
*	Bug fix: Jig SORT_DESC (Issue #233)
*	Bug fix: Error reporting (Issue #225)
*	Bug fix: language() return value

3.0.3 (29 Dec 2013)
*	NEW: [ajax] and [sync] routing pattern modifiers
*	NEW: Basket class (session-based pseudo-mapper, shopping cart, etc.)
*	NEW: Test->message() method
*	NEW: DB profiling via DB->log()
*	NEW: Matrix->calendar()
*	NEW: Audit->card() and Audit->mod10() for credit card verification
*	NEW: Geo->weather()
*	NEW: Base->relay() accepts comma-separated callbacks; but unlike
	Base->chain(), result of previous callback becomes argument of the next
*	Numerous performance tweaks
*	Interoperability with new MongoClient class
*	Web->request() now recognizes gzip and deflate encoding
*	Differences in behavior of Web->request() engines rectified
*	mutex() now uses an ID as argument (instead of filename to make it clear
	that specified file is not the target being locked, but a primitive
	cross-platform semaphore)
*	DB\SQL\Mapper field _id now returned even in the absence of any
	auto-increment field
*	Magic class spinned off as a separate file
*	ISO 3166-1 alpha-2 table updated
*	Apache redirect emulation for PHP 5.4 CLI server mode
*	Framework instance now passed as argument to any user-defined shutdown
	function
*	Cache engine now used as storage for Web->minify() output
*	Flag added for enabling/disabling Image class filter history
*	Bug fix: Trailing routing token consumes HTTP query
*	Bug fix: LANGUAGE spills over to LOCALES setting
*	Bug fix: Inconsistent dry() return value
*	Bug fix: URL-decoding

3.0.2 (23 Dec 2013)
*	NEW: Syntax-highlighted stack traces via Base->highlight(); boolean
	HIGHLIGHT global variable can be used to enable/disable this feature
*	NEW: Template engine <ignore> tag
*	NEW: Image->captcha()
*	NEW: DNSBL-based spammer detection (ported from 2.x)
*	NEW: paginate(), first(), and last() methods for data mappers
*	NEW: X-HTTP-Method-Override header now recognized
*	NEW: Base->chain() method for executing callbacks in succession
*	NEW: HOST global variable; derived from either $_SERVER['SERVER_NAME'] or
	gethostname()
*	NEW: REALM global variable representing full canonical URI
*	NEW: Auth plug-in
*	NEW: Pingback plug-in (implements both Pingback 1.0 protocol client and
	server)
*	NEW: DEBUG verbosity can now reach up to level 3; Base->stringify() drills
	down to object properties at this setting
*	NEW: HTTP PATCH method added to recognized HTTP ReST methods
*	Web->slug() now trims trailing dashes
*	Web->request() now allows relative local URLs as argument
*	Use of PARAMS in route handlers now unnecessary; framework now passes two
	arguments to route handlers: the framework object instance and an array
	containing the captured values of tokens in route patterns
*	Standardized timeout settings among Web->request() backends
*	Session IDs regenerated for additional security
*	Automatic HTTP 404 responses by Base->call() now restricted to route
	handlers
*	Empty comments in ini-style files now parsed properly
*	Use file_get_contents() in methods that don't involve high concurrency

3.0.1 (14 Dec 2013)
*	Major rewrite of much of the framework's core features
diff --git a/vendor/fatfree-3.6/lib/COPYING b/vendor/fatfree-3.6/lib/COPYING
new file mode 100755
index 0000000..3c7236c
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/COPYING
@@ -0,0 +1,621 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007

Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

Preamble

The GNU General Public License is a free, copyleft license for
software and other kinds of works.

The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.

When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.

Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and
modification follow.

TERMS AND CONDITIONS

0. Definitions.

"This License" refers to version 3 of the GNU General Public License.

"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.

To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

A "covered work" means either the unmodified Program or a work based
on the Program.

To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

1. Source Code.

The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.

A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

The Corresponding Source for a work in source code form is that
same work.

2. Basic Permissions.

All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.

3. Protecting Users' Legal Rights From Anti-Circumvention Law.

No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

4. Conveying Verbatim Copies.

You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

5. Conveying Modified Source Versions.

You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.

b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".

c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.

d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.

A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

6. Conveying Non-Source Forms.

You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.

b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.

c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.

d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.

e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.

A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

7. Additional Terms.

"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or

b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or

c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or

d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or

e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or

f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.

All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

8. Termination.

You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

9. Acceptance Not Required for Having Copies.

You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

10. Automatic Licensing of Downstream Recipients.

Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.

An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

11. Patents.

A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".

A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

12. No Surrender of Others' Freedom.

If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

13. Use with the GNU Affero General Public License.

Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

14. Revised Versions of this License.

The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

15. Disclaimer of Warranty.

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. Limitation of Liability.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

17. Interpretation of Sections 15 and 16.

If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS
diff --git a/vendor/fatfree-3.6/lib/audit.php b/vendor/fatfree-3.6/lib/audit.php
new file mode 100755
index 0000000..0e69b56
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/audit.php
@@ -0,0 +1,191 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Data validator
class Audit extends Prefab {

	//@{ User agents
	const
		UA_Mobile='android|blackberry|phone|ipod|palm|windows\s+ce',
		UA_Desktop='bsd|linux|os\s+[x9]|solaris|windows',
		UA_Bot='bot|crawl|slurp|spider';
	//@}

	/**
	*	Return TRUE if string is a valid URL
	*	@return bool
	*	@param $str string
	**/
	function url($str) {
		return is_string(filter_var($str,FILTER_VALIDATE_URL));
	}

	/**
	*	Return TRUE if string is a valid e-mail address;
	*	Check DNS MX records if specified
	*	@return bool
	*	@param $str string
	*	@param $mx boolean
	**/
	function email($str,$mx=TRUE) {
		$hosts=[];
		return is_string(filter_var($str,FILTER_VALIDATE_EMAIL)) &&
			(!$mx || getmxrr(substr($str,strrpos($str,'@')+1),$hosts));
	}

	/**
	*	Return TRUE if string is a valid IPV4 address
	*	@return bool
	*	@param $addr string
	**/
	function ipv4($addr) {
		return (bool)filter_var($addr,FILTER_VALIDATE_IP,FILTER_FLAG_IPV4);
	}

	/**
	*	Return TRUE if string is a valid IPV6 address
	*	@return bool
	*	@param $addr string
	**/
	function ipv6($addr) {
		return (bool)filter_var($addr,FILTER_VALIDATE_IP,FILTER_FLAG_IPV6);
	}

	/**
	*	Return TRUE if IP address is within private range
	*	@return bool
	*	@param $addr string
	**/
	function isprivate($addr) {
		return !(bool)filter_var($addr,FILTER_VALIDATE_IP,
			FILTER_FLAG_IPV4|FILTER_FLAG_IPV6|FILTER_FLAG_NO_PRIV_RANGE);
	}

	/**
	*	Return TRUE if IP address is within reserved range
	*	@return bool
	*	@param $addr string
	**/
	function isreserved($addr) {
		return !(bool)filter_var($addr,FILTER_VALIDATE_IP,
			FILTER_FLAG_IPV4|FILTER_FLAG_IPV6|FILTER_FLAG_NO_RES_RANGE);
	}

	/**
	*	Return TRUE if IP address is neither private nor reserved
	*	@return bool
	*	@param $addr string
	**/
	function ispublic($addr) {
		return (bool)filter_var($addr,FILTER_VALIDATE_IP,
			FILTER_FLAG_IPV4|FILTER_FLAG_IPV6|
			FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE);
	}

	/**
	*	Return TRUE if user agent is a desktop browser
	*	@return bool
	*	@param $agent string
	**/
	function isdesktop($agent=NULL) {
		if (!isset($agent))
			$agent=Base::instance()->AGENT;
		return (bool)preg_match('/('.self::UA_Desktop.')/i',$agent) &&
			!$this->ismobile($agent);
	}

	/**
	*	Return TRUE if user agent is a mobile device
	*	@return bool
	*	@param $agent string
	**/
	function ismobile($agent=NULL) {
		if (!isset($agent))
			$agent=Base::instance()->AGENT;
		return (bool)preg_match('/('.self::UA_Mobile.')/i',$agent);
	}

	/**
	*	Return TRUE if user agent is a Web bot
	*	@return bool
	*	@param $agent string
	**/
	function isbot($agent=NULL) {
		if (!isset($agent))
			$agent=Base::instance()->AGENT;
		return (bool)preg_match('/('.self::UA_Bot.')/i',$agent);
	}

	/**
	*	Return TRUE if specified ID has a valid (Luhn) Mod-10 check digit
	*	@return bool
	*	@param $id string
	**/
	function mod10($id) {
		if (!ctype_digit($id))
			return FALSE;
		$id=strrev($id);
		$sum=0;
		for ($i=0,$l=strlen($id);$i<$l;$i++)
			$sum+=$id[$i]+$i%2*(($id[$i]>4)*-4+$id[$i]%5);
		return !($sum%10);
	}

	/**
	*	Return credit card type if number is valid
	*	@return string|FALSE
	*	@param $id string
	**/
	function card($id) {
		$id=preg_replace('/[^\d]/','',$id);
		if ($this->mod10($id)) {
			if (preg_match('/^3[47][0-9]{13}$/',$id))
				return 'American Express';
			if (preg_match('/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/',$id))
				return 'Diners Club';
			if (preg_match('/^6(?:011|5[0-9][0-9])[0-9]{12}$/',$id))
				return 'Discover';
			if (preg_match('/^(?:2131|1800|35\d{3})\d{11}$/',$id))
				return 'JCB';
			if (preg_match('/^5[1-5][0-9]{14}$|'.
				'^(222[1-9]|2[3-6]\d{2}|27[0-1]\d|2720)\d{12}$/',$id))
				return 'MasterCard';
			if (preg_match('/^4[0-9]{12}(?:[0-9]{3})?$/',$id))
				return 'Visa';
		}
		return FALSE;
	}

	/**
	*	Return entropy estimate of a password (NIST 800-63)
	*	@return int|float
	*	@param $str string
	**/
	function entropy($str) {
		$len=strlen($str);
		return 4*min($len,1)+($len>1?(2*(min($len,8)-1)):0)+
			($len>8?(1.5*(min($len,20)-8)):0)+($len>20?($len-20):0)+
			6*(bool)(preg_match(
				'/[A-Z].*?[0-9[:punct:]]|[0-9[:punct:]].*?[A-Z]/',$str));
	}

}
diff --git a/vendor/fatfree-3.6/lib/auth.php b/vendor/fatfree-3.6/lib/auth.php
new file mode 100755
index 0000000..c1e8a57
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/auth.php
@@ -0,0 +1,249 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Authorization/authentication plug-in
class Auth {

	//@{ Error messages
	const
		E_LDAP='LDAP connection failure',
		E_SMTP='SMTP connection failure';
	//@}

	protected
		//! Auth storage
		$storage,
		//! Mapper object
		$mapper,
		//! Storage options
		$args;

	/**
	*	Jig storage handler
	*	@return bool
	*	@param $id string
	*	@param $pw string
	*	@param $realm string
	**/
	protected function _jig($id,$pw,$realm) {
		return (bool)
			call_user_func_array(
				[$this->mapper,'load'],
				[
					array_merge(
						[
							'@'.$this->args['id'].'==? AND '.
							'@'.$this->args['pw'].'==?'.
							(isset($this->args['realm'])?
								(' AND @'.$this->args['realm'].'==?'):''),
							$id,$pw
						],
						(isset($this->args['realm'])?[$realm]:[])
					)
				]
			);
	}

	/**
	*	MongoDB storage handler
	*	@return bool
	*	@param $id string
	*	@param $pw string
	*	@param $realm string
	**/
	protected function _mongo($id,$pw,$realm) {
		return (bool)
			$this->mapper->load(
				[
					$this->args['id']=>$id,
					$this->args['pw']=>$pw
				]+
				(isset($this->args['realm'])?
					[$this->args['realm']=>$realm]:[])
			);
	}

	/**
	*	SQL storage handler
	*	@return bool
	*	@param $id string
	*	@param $pw string
	*	@param $realm string
	**/
	protected function _sql($id,$pw,$realm) {
		return (bool)
			call_user_func_array(
				[$this->mapper,'load'],
				[
					array_merge(
						[
							$this->args['id'].'=? AND '.
							$this->args['pw'].'=?'.
							(isset($this->args['realm'])?
								(' AND '.$this->args['realm'].'=?'):''),
							$id,$pw
						],
						(isset($this->args['realm'])?[$realm]:[])
					)
				]
			);
	}

	/**
	*	LDAP storage handler
	*	@return bool
	*	@param $id string
	*	@param $pw string
	**/
	protected function _ldap($id,$pw) {
		$port=(int)($this->args['port']?:389);
		$filter=$this->args['filter']=$this->args['filter']?:"uid=".$id;
		$this->args['attr']=$this->args['attr']?:["uid"];
		array_walk($this->args['attr'],
		function($attr)use(&$filter,$id) {
			$filter=str_ireplace($attr."=*",$attr."=".$id,$filter);});
		$dc=@ldap_connect($this->args['dc'],$port);
		if ($dc &&
			ldap_set_option($dc,LDAP_OPT_PROTOCOL_VERSION,3) &&
			ldap_set_option($dc,LDAP_OPT_REFERRALS,0) &&
			ldap_bind($dc,$this->args['rdn'],$this->args['pw']) &&
			($result=ldap_search($dc,$this->args['base_dn'],
				$filter,$this->args['attr'])) &&
			ldap_count_entries($dc,$result) &&
			($info=ldap_get_entries($dc,$result)) &&
			$info['count']==1 &&
			@ldap_bind($dc,$info[0]['dn'],$pw) &&
			@ldap_close($dc)) {
			return in_array($id,(array_map(function($value){return $value[0];},
				array_intersect_key($info[0],
					array_flip($this->args['attr'])))),TRUE);
		}
		user_error(self::E_LDAP,E_USER_ERROR);
	}

	/**
	*	SMTP storage handler
	*	@return bool
	*	@param $id string
	*	@param $pw string
	**/
	protected function _smtp($id,$pw) {
		$socket=@fsockopen(
			(strtolower($this->args['scheme'])=='ssl'?
				'ssl://':'').$this->args['host'],
				$this->args['port']);
		$dialog=function($cmd=NULL) use($socket) {
			if (!is_null($cmd))
				fputs($socket,$cmd."\r\n");
			$reply='';
			while (!feof($socket) &&
				($info=stream_get_meta_data($socket)) &&
				!$info['timed_out'] && $str=fgets($socket,4096)) {
				$reply.=$str;
				if (preg_match('/(?:^|\n)\d{3} .+\r\n/s',
					$reply))
					break;
			}
			return $reply;
		};
		if ($socket) {
			stream_set_blocking($socket,TRUE);
			$dialog();
			$fw=Base::instance();
			$dialog('EHLO '.$fw->HOST);
			if (strtolower($this->args['scheme'])=='tls') {
				$dialog('STARTTLS');
				stream_socket_enable_crypto(
					$socket,TRUE,STREAM_CRYPTO_METHOD_TLS_CLIENT);
				$dialog('EHLO '.$fw->HOST);
			}
			// Authenticate
			$dialog('AUTH LOGIN');
			$dialog(base64_encode($id));
			$reply=$dialog(base64_encode($pw));
			$dialog('QUIT');
			fclose($socket);
			return (bool)preg_match('/^235 /',$reply);
		}
		user_error(self::E_SMTP,E_USER_ERROR);
	}

	/**
	*	Login auth mechanism
	*	@return bool
	*	@param $id string
	*	@param $pw string
	*	@param $realm string
	**/
	function login($id,$pw,$realm=NULL) {
		return $this->{'_'.$this->storage}($id,$pw,$realm);
	}

	/**
	*	HTTP basic auth mechanism
	*	@return bool
	*	@param $func callback
	**/
	function basic($func=NULL) {
		$fw=Base::instance();
		$realm=$fw->REALM;
		$hdr=NULL;
		if (isset($_SERVER['HTTP_AUTHORIZATION']))
			$hdr=$_SERVER['HTTP_AUTHORIZATION'];
		elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
			$hdr=$_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
		if (!empty($hdr))
			list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW'])=
				explode(':',base64_decode(substr($hdr,6)));
		if (isset($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) &&
			$this->login(
				$_SERVER['PHP_AUTH_USER'],
				$func?
					$fw->call($func,$_SERVER['PHP_AUTH_PW']):
					$_SERVER['PHP_AUTH_PW'],
				$realm
			))
			return TRUE;
		if (PHP_SAPI!='cli')
			header('WWW-Authenticate: Basic realm="'.$realm.'"');
		$fw->status(401);
		return FALSE;
	}

	/**
	*	Instantiate class
	*	@return object
	*	@param $storage string|object
	*	@param $args array
	**/
	function __construct($storage,array $args=NULL) {
		if (is_object($storage) && is_a($storage,'DB\Cursor')) {
			$this->storage=$storage->dbtype();
			$this->mapper=$storage;
			unset($ref);
		}
		else
			$this->storage=$storage;
		$this->args=$args;
	}

}
diff --git a/vendor/fatfree-3.6/lib/base.php b/vendor/fatfree-3.6/lib/base.php
new file mode 100755
index 0000000..e04abf4
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/base.php
@@ -0,0 +1,3453 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Factory class for single-instance objects
abstract class Prefab {

	/**
	*	Return class instance
	*	@return static
	**/
	static function instance() {
		if (!Registry::exists($class=get_called_class())) {
			$ref=new Reflectionclass($class);
			$args=func_get_args();
			Registry::set($class,
				$args?$ref->newinstanceargs($args):new $class);
		}
		return Registry::get($class);
	}

}

//! Base structure
final class Base extends Prefab implements ArrayAccess {

	//@{ Framework details
	const
		PACKAGE='Fat-Free Framework',
		VERSION='3.6.4-Release';
	//@}

	//@{ HTTP status codes (RFC 2616)
	const
		HTTP_100='Continue',
		HTTP_101='Switching Protocols',
		HTTP_103='Early Hints',
		HTTP_200='OK',
		HTTP_201='Created',
		HTTP_202='Accepted',
		HTTP_203='Non-Authorative Information',
		HTTP_204='No Content',
		HTTP_205='Reset Content',
		HTTP_206='Partial Content',
		HTTP_300='Multiple Choices',
		HTTP_301='Moved Permanently',
		HTTP_302='Found',
		HTTP_303='See Other',
		HTTP_304='Not Modified',
		HTTP_305='Use Proxy',
		HTTP_307='Temporary Redirect',
		HTTP_400='Bad Request',
		HTTP_401='Unauthorized',
		HTTP_402='Payment Required',
		HTTP_403='Forbidden',
		HTTP_404='Not Found',
		HTTP_405='Method Not Allowed',
		HTTP_406='Not Acceptable',
		HTTP_407='Proxy Authentication Required',
		HTTP_408='Request Timeout',
		HTTP_409='Conflict',
		HTTP_410='Gone',
		HTTP_411='Length Required',
		HTTP_412='Precondition Failed',
		HTTP_413='Request Entity Too Large',
		HTTP_414='Request-URI Too Long',
		HTTP_415='Unsupported Media Type',
		HTTP_416='Requested Range Not Satisfiable',
		HTTP_417='Expectation Failed',
		HTTP_500='Internal Server Error',
		HTTP_501='Not Implemented',
		HTTP_502='Bad Gateway',
		HTTP_503='Service Unavailable',
		HTTP_504='Gateway Timeout',
		HTTP_505='HTTP Version Not Supported';
	//@}

	const
		//! Mapped PHP globals
		GLOBALS='GET|POST|COOKIE|REQUEST|SESSION|FILES|SERVER|ENV',
		//! HTTP verbs
		VERBS='GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT|OPTIONS',
		//! Default directory permissions
		MODE=0755,
		//! Syntax highlighting stylesheet
		CSS='code.css';

	//@{ Request types
	const
		REQ_SYNC=1,
		REQ_AJAX=2,
		REQ_CLI=4;
	//@}

	//@{ Error messages
	const
		E_Pattern='Invalid routing pattern: %s',
		E_Named='Named route does not exist: %s',
		E_Fatal='Fatal error: %s',
		E_Open='Unable to open %s',
		E_Routes='No routes specified',
		E_Class='Invalid class %s',
		E_Method='Invalid method %s',
		E_Hive='Invalid hive key %s';
	//@}

	private
		//! Globals
		$hive,
		//! Initial settings
		$init,
		//! Language lookup sequence
		$languages,
		//! Mutex locks
		$locks=[],
		//! Default fallback language
		$fallback='en';

	/**
	*	Sync PHP global with corresponding hive key
	*	@return array
	*	@param $key string
	**/
	function sync($key) {
		return $this->hive[$key]=&$GLOBALS['_'.$key];
	}

	/**
	*	Return the parts of specified hive key
	*	@return array
	*	@param $key string
	**/
	private function cut($key) {
		return preg_split('/\[\h*[\'"]?(.+?)[\'"]?\h*\]|(->)|\./',
			$key,NULL,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
	}

	/**
	*	Replace tokenized URL with available token values
	*	@return string
	*	@param $url array|string
	*	@param $args array
	**/
	function build($url,$args=[]) {
		$args+=$this->hive['PARAMS'];
		if (is_array($url))
			foreach ($url as &$var) {
				$var=$this->build($var,$args);
				unset($var);
			}
		else {
			$i=0;
			$url=preg_replace_callback('/@(\w+)|(\*)/',
				function($match) use(&$i,$args) {
					if (isset($match[1]) &&
						array_key_exists($match[1],$args))
						return $args[$match[1]];
					if (isset($match[2]) &&
						array_key_exists($match[2],$args)) {
						if (!is_array($args[$match[2]]))
							return $args[$match[2]];
						$i++;
						return $args[$match[2]][$i-1];
					}
					return $match[0];
				},$url);
		}
		return $url;
	}

	/**
	*	Parse string containing key-value pairs
	*	@return array
	*	@param $str string
	**/
	function parse($str) {
		preg_match_all('/(\w+|\*)\h*=\h*(?:\[(.+?)\]|(.+?))(?=,|$)/',
			$str,$pairs,PREG_SET_ORDER);
		$out=[];
		foreach ($pairs as $pair)
			if ($pair[2]) {
				$out[$pair[1]]=[];
				foreach (explode(',',$pair[2]) as $val)
					array_push($out[$pair[1]],$val);
			}
			else
				$out[$pair[1]]=trim($pair[3]);
		return $out;
	}

	/**
	 * cast string variable to php type or constant
	 * @param $val
	 * @return mixed
	 */
	function cast($val) {
		if (is_numeric($val))
			return $val+0;
		$val=trim($val);
		if (preg_match('/^\w+$/i',$val) && defined($val))
			return constant($val);
		return $val;
	}

	/**
	*	Convert JS-style token to PHP expression
	*	@return string
	*	@param $str string
	**/
	function compile($str) {
		return preg_replace_callback(
			'/(?<!\w)@(\w+(?:(?:\->|::)\w+)?)'.
			'((?:\.\w+|\[(?:(?:[^\[\]]*|(?R))*)\]|(?:\->|::)\w+|\()*)/',
			function($expr) {
				$str='$'.$expr[1];
				if (isset($expr[2]))
					$str.=preg_replace_callback(
						'/\.(\w+)(\()?|\[((?:[^\[\]]*|(?R))*)\]/',
						function($sub) {
							if (empty($sub[2])) {
								if (ctype_digit($sub[1]))
									$sub[1]=(int)$sub[1];
								$out='['.
									(isset($sub[3])?
										$this->compile($sub[3]):
										var_export($sub[1],TRUE)).
								']';
							}
							else
								$out=function_exists($sub[1])?
									$sub[0]:
									('['.var_export($sub[1],TRUE).']'.$sub[2]);
							return $out;
						},
						$expr[2]
					);
				return $str;
			},
			$str
		);
	}

	/**
	*	Get hive key reference/contents; Add non-existent hive keys,
	*	array elements, and object properties by default
	*	@return mixed
	*	@param $key string
	*	@param $add bool
	*	@param $var mixed
	**/
	function &ref($key,$add=TRUE,&$var=NULL) {
		$null=NULL;
		$parts=$this->cut($key);
		if ($parts[0]=='SESSION') {
			if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
				session_start();
			$this->sync('SESSION');
		}
		elseif (!preg_match('/^\w+$/',$parts[0]))
			user_error(sprintf(self::E_Hive,$this->stringify($key)),
				E_USER_ERROR);
		if (is_null($var)) {
			if ($add)
				$var=&$this->hive;
			else
				$var=$this->hive;
		}
		$obj=FALSE;
		foreach ($parts as $part)
			if ($part=='->')
				$obj=TRUE;
			elseif ($obj) {
				$obj=FALSE;
				if (!is_object($var))
					$var=new stdclass;
				if ($add || property_exists($var,$part))
					$var=&$var->$part;
				else {
					$var=&$null;
					break;
				}
			}
			else {
				if (!is_array($var))
					$var=[];
				if ($add || array_key_exists($part,$var))
					$var=&$var[$part];
				else {
					$var=&$null;
					break;
				}
			}
		return $var;
	}

	/**
	*	Return TRUE if hive key is set
	*	(or return timestamp and TTL if cached)
	*	@return bool
	*	@param $key string
	*	@param $val mixed
	**/
	function exists($key,&$val=NULL) {
		$val=$this->ref($key,FALSE);
		return isset($val)?
			TRUE:
			(Cache::instance()->exists($this->hash($key).'.var',$val)?:FALSE);
	}

	/**
	*	Return TRUE if hive key is empty and not cached
	*	@param $key string
	*	@param $val mixed
	*	@return bool
	**/
	function devoid($key,&$val=NULL) {
		$val=$this->ref($key,FALSE);
		return empty($val) &&
			(!Cache::instance()->exists($this->hash($key).'.var',$val) ||
				!$val);
	}

	/**
	*	Bind value to hive key
	*	@return mixed
	*	@param $key string
	*	@param $val mixed
	*	@param $ttl int
	**/
	function set($key,$val,$ttl=0) {
		$time=(int)$this->hive['TIME'];
		if (preg_match('/^(GET|POST|COOKIE)\b(.+)/',$key,$expr)) {
			$this->set('REQUEST'.$expr[2],$val);
			if ($expr[1]=='COOKIE') {
				$parts=$this->cut($key);
				$jar=$this->unserialize($this->serialize($this->hive['JAR']));
				unset($jar['lifetime']);
				if (isset($_COOKIE[$parts[1]]))
					call_user_func_array('setcookie',
						array_merge([$parts[1],NULL],['expire'=>0]+$jar));
				if ($ttl)
					$jar['expire']=$time+$ttl;
				call_user_func_array('setcookie',[$parts[1],$val]+$jar);
				$_COOKIE[$parts[1]]=$val;
				return $val;
			}
		}
		else switch ($key) {
		case 'CACHE':
			$val=Cache::instance()->load($val);
			break;
		case 'ENCODING':
			ini_set('default_charset',$val);
			if (extension_loaded('mbstring'))
				mb_internal_encoding($val);
			break;
		case 'FALLBACK':
			$this->fallback=$val;
			$lang=$this->language($this->hive['LANGUAGE']);
		case 'LANGUAGE':
			if (!isset($lang))
				$val=$this->language($val);
			$lex=$this->lexicon($this->hive['LOCALES'],$ttl);
		case 'LOCALES':
			if (isset($lex) || $lex=$this->lexicon($val,$ttl))
				foreach ($lex as $dt=>$dd) {
					$ref=&$this->ref($this->hive['PREFIX'].$dt);
					$ref=$dd;
					unset($ref);
				}
			break;
		case 'TZ':
			date_default_timezone_set($val);
			break;
		}
		$ref=&$this->ref($key);
		$ref=$val;
		if (preg_match('/^JAR\b/',$key)) {
			if ($key=='JAR.lifetime')
				$this->set('JAR.expire',$val==0?0:
					(is_int($val)?$time+$val:strtotime($val)));
			else {
				if ($key=='JAR.expire')
					$this->hive['JAR']['lifetime']=max(0,$val-$time);
				$jar=$this->unserialize($this->serialize($this->hive['JAR']));
				unset($jar['expire']);
				if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
					call_user_func_array('session_set_cookie_params',$jar);
			}
		}
		if ($ttl)
			// Persist the key-value pair
			Cache::instance()->set($this->hash($key).'.var',$val,$ttl);
		return $ref;
	}

	/**
	*	Retrieve contents of hive key
	*	@return mixed
	*	@param $key string
	*	@param $args string|array
	**/
	function get($key,$args=NULL) {
		if (is_string($val=$this->ref($key,FALSE)) && !is_null($args))
			return call_user_func_array(
				[$this,'format'],
				array_merge([$val],is_array($args)?$args:[$args])
			);
		if (is_null($val)) {
			// Attempt to retrieve from cache
			if (Cache::instance()->exists($this->hash($key).'.var',$data))
				return $data;
		}
		return $val;
	}

	/**
	*	Unset hive key
	*	@return NULL
	*	@param $key string
	**/
	function clear($key) {
		// Normalize array literal
		$cache=Cache::instance();
		$parts=$this->cut($key);
		if ($key=='CACHE')
			// Clear cache contents
			$cache->reset();
		elseif (preg_match('/^(GET|POST|COOKIE)\b(.+)/',$key,$expr)) {
			$this->clear('REQUEST'.$expr[2]);
			if ($expr[1]=='COOKIE') {
				$parts=$this->cut($key);
				$jar=$this->hive['JAR'];
				unset($jar['lifetime']);
				$jar['expire']=0;
				call_user_func_array('setcookie',
					array_merge([$parts[1],NULL],$jar));
				unset($_COOKIE[$parts[1]]);
			}
		}
		elseif ($parts[0]=='SESSION') {
			if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
				session_start();
			if (empty($parts[1])) {
				// End session
				session_unset();
				session_destroy();
				$this->clear('COOKIE.'.session_name());
			}
			$this->sync('SESSION');
		}
		if (!isset($parts[1]) && array_key_exists($parts[0],$this->init))
			// Reset global to default value
			$this->hive[$parts[0]]=$this->init[$parts[0]];
		else {
			eval('unset('.$this->compile('@this->hive.'.$key).');');
			if ($parts[0]=='SESSION') {
				session_commit();
				session_start();
			}
			if ($cache->exists($hash=$this->hash($key).'.var'))
				// Remove from cache
				$cache->clear($hash);
		}
	}

	/**
	*	Return TRUE if hive variable is 'on'
	*	@return bool
	*	@param $key string
	**/
	function checked($key) {
		$ref=&$this->ref($key);
		return $ref=='on';
	}

	/**
	*	Return TRUE if property has public visibility
	*	@return bool
	*	@param $obj object
	*	@param $key string
	**/
	function visible($obj,$key) {
		if (property_exists($obj,$key)) {
			$ref=new ReflectionProperty(get_class($obj),$key);
			$out=$ref->ispublic();
			unset($ref);
			return $out;
		}
		return FALSE;
	}

	/**
	*	Multi-variable assignment using associative array
	*	@return NULL
	*	@param $vars array
	*	@param $prefix string
	*	@param $ttl int
	**/
	function mset(array $vars,$prefix='',$ttl=0) {
		foreach ($vars as $key=>$val)
			$this->set($prefix.$key,$val,$ttl);
	}

	/**
	*	Publish hive contents
	*	@return array
	**/
	function hive() {
		return $this->hive;
	}

	/**
	*	Copy contents of hive variable to another
	*	@return mixed
	*	@param $src string
	*	@param $dst string
	**/
	function copy($src,$dst) {
		$ref=&$this->ref($dst);
		return $ref=$this->ref($src,FALSE);
	}

	/**
	*	Concatenate string to hive string variable
	*	@return string
	*	@param $key string
	*	@param $val string
	**/
	function concat($key,$val) {
		$ref=&$this->ref($key);
		$ref.=$val;
		return $ref;
	}

	/**
	*	Swap keys and values of hive array variable
	*	@return array
	*	@param $key string
	*	@public
	**/
	function flip($key) {
		$ref=&$this->ref($key);
		return $ref=array_combine(array_values($ref),array_keys($ref));
	}

	/**
	*	Add element to the end of hive array variable
	*	@return mixed
	*	@param $key string
	*	@param $val mixed
	**/
	function push($key,$val) {
		$ref=&$this->ref($key);
		$ref[]=$val;
		return $val;
	}

	/**
	*	Remove last element of hive array variable
	*	@return mixed
	*	@param $key string
	**/
	function pop($key) {
		$ref=&$this->ref($key);
		return array_pop($ref);
	}

	/**
	*	Add element to the beginning of hive array variable
	*	@return mixed
	*	@param $key string
	*	@param $val mixed
	**/
	function unshift($key,$val) {
		$ref=&$this->ref($key);
		array_unshift($ref,$val);
		return $val;
	}

	/**
	*	Remove first element of hive array variable
	*	@return mixed
	*	@param $key string
	**/
	function shift($key) {
		$ref=&$this->ref($key);
		return array_shift($ref);
	}

	/**
	*	Merge array with hive array variable
	*	@return array
	*	@param $key string
	*	@param $src string|array
	*	@param $keep bool
	**/
	function merge($key,$src,$keep=FALSE) {
		$ref=&$this->ref($key);
		if (!$ref)
			$ref=[];
		$out=array_merge($ref,is_string($src)?$this->hive[$src]:$src);
		if ($keep)
			$ref=$out;
		return $out;
	}

	/**
	*	Extend hive array variable with default values from $src
	*	@return array
	*	@param $key string
	*	@param $src string|array
	*	@param $keep bool
	**/
	function extend($key,$src,$keep=FALSE) {
		$ref=&$this->ref($key);
		if (!$ref)
			$ref=[];
		$out=array_replace_recursive(
			is_string($src)?$this->hive[$src]:$src,$ref);
		if ($keep)
			$ref=$out;
		return $out;
	}

	/**
	*	Convert backslashes to slashes
	*	@return string
	*	@param $str string
	**/
	function fixslashes($str) {
		return $str?strtr($str,'\\','/'):$str;
	}

	/**
	*	Split comma-, semi-colon, or pipe-separated string
	*	@return array
	*	@param $str string
	*	@param $noempty bool
	**/
	function split($str,$noempty=TRUE) {
		return array_map('trim',
			preg_split('/[,;|]/',$str,0,$noempty?PREG_SPLIT_NO_EMPTY:0));
	}

	/**
	*	Convert PHP expression/value to compressed exportable string
	*	@return string
	*	@param $arg mixed
	*	@param $stack array
	**/
	function stringify($arg,array $stack=NULL) {
		if ($stack) {
			foreach ($stack as $node)
				if ($arg===$node)
					return '*RECURSION*';
		}
		else
			$stack=[];
		switch (gettype($arg)) {
			case 'object':
				$str='';
				foreach (get_object_vars($arg) as $key=>$val)
					$str.=($str?',':'').
						var_export($key,TRUE).'=>'.
						$this->stringify($val,
							array_merge($stack,[$arg]));
				return get_class($arg).'::__set_state(['.$str.'])';
			case 'array':
				$str='';
				$num=isset($arg[0]) &&
					ctype_digit(implode('',array_keys($arg)));
				foreach ($arg as $key=>$val)
					$str.=($str?',':'').
						($num?'':(var_export($key,TRUE).'=>')).
						$this->stringify($val,array_merge($stack,[$arg]));
				return '['.$str.']';
			default:
				return var_export($arg,TRUE);
		}
	}

	/**
	*	Flatten array values and return as CSV string
	*	@return string
	*	@param $args array
	**/
	function csv(array $args) {
		return implode(',',array_map('stripcslashes',
			array_map([$this,'stringify'],$args)));
	}

	/**
	*	Convert snakecase string to camelcase
	*	@return string
	*	@param $str string
	**/
	function camelcase($str) {
		return preg_replace_callback(
			'/_(\pL)/u',
			function($match) {
				return strtoupper($match[1]);
			},
			$str
		);
	}

	/**
	*	Convert camelcase string to snakecase
	*	@return string
	*	@param $str string
	**/
	function snakecase($str) {
		return strtolower(preg_replace('/(?!^)\p{Lu}/u','_\0',$str));
	}

	/**
	*	Return -1 if specified number is negative, 0 if zero,
	*	or 1 if the number is positive
	*	@return int
	*	@param $num mixed
	**/
	function sign($num) {
		return $num?($num/abs($num)):0;
	}

	/**
	*	Extract values of array whose keys start with the given prefix
	*	@return array
	*	@param $arr array
	*	@param $prefix string
	**/
	function extract($arr,$prefix) {
		$out=[];
		foreach (preg_grep('/^'.preg_quote($prefix,'/').'/',array_keys($arr))
			as $key)
			$out[substr($key,strlen($prefix))]=$arr[$key];
		return $out;
	}

	/**
	*	Convert class constants to array
	*	@return array
	*	@param $class object|string
	*	@param $prefix string
	**/
	function constants($class,$prefix='') {
		$ref=new ReflectionClass($class);
		return $this->extract($ref->getconstants(),$prefix);
	}

	/**
	*	Generate 64bit/base36 hash
	*	@return string
	*	@param $str
	**/
	function hash($str) {
		return str_pad(base_convert(
			substr(sha1($str),-16),16,36),11,'0',STR_PAD_LEFT);
	}

	/**
	*	Return Base64-encoded equivalent
	*	@return string
	*	@param $data string
	*	@param $mime string
	**/
	function base64($data,$mime) {
		return 'data:'.$mime.';base64,'.base64_encode($data);
	}

	/**
	*	Convert special characters to HTML entities
	*	@return string
	*	@param $str string
	**/
	function encode($str) {
		return @htmlspecialchars($str,$this->hive['BITMASK'],
			$this->hive['ENCODING'])?:$this->scrub($str);
	}

	/**
	*	Convert HTML entities back to characters
	*	@return string
	*	@param $str string
	**/
	function decode($str) {
		return htmlspecialchars_decode($str,$this->hive['BITMASK']);
	}

	/**
	*	Invoke callback recursively for all data types
	*	@return mixed
	*	@param $arg mixed
	*	@param $func callback
	*	@param $stack array
	**/
	function recursive($arg,$func,$stack=[]) {
		if ($stack) {
			foreach ($stack as $node)
				if ($arg===$node)
					return $arg;
		}
		switch (gettype($arg)) {
			case 'object':
				$ref=new ReflectionClass($arg);
				if ($ref->iscloneable()) {
					$arg=clone($arg);
					$cast=is_a($arg,'IteratorAggregate')?
						iterator_to_array($arg):get_object_vars($arg);
					foreach ($cast as $key=>$val)
						$arg->$key=$this->recursive(
							$val,$func,array_merge($stack,[$arg]));
				}
				return $arg;
			case 'array':
				$copy=[];
				foreach ($arg as $key=>$val)
					$copy[$key]=$this->recursive($val,$func,
						array_merge($stack,[$arg]));
				return $copy;
		}
		return $func($arg);
	}

	/**
	*	Remove HTML tags (except those enumerated) and non-printable
	*	characters to mitigate XSS/code injection attacks
	*	@return mixed
	*	@param $arg mixed
	*	@param $tags string
	**/
	function clean($arg,$tags=NULL) {
		return $this->recursive($arg,
			function($val) use($tags) {
				if ($tags!='*')
					$val=trim(strip_tags($val,
						'<'.implode('><',$this->split($tags)).'>'));
				return trim(preg_replace(
					'/[\x00-\x08\x0B\x0C\x0E-\x1F]/','',$val));
			}
		);
	}

	/**
	*	Similar to clean(), except that variable is passed by reference
	*	@return mixed
	*	@param $var mixed
	*	@param $tags string
	**/
	function scrub(&$var,$tags=NULL) {
		return $var=$this->clean($var,$tags);
	}

	/**
	*	Return locale-aware formatted string
	*	@return string
	**/
	function format() {
		$args=func_get_args();
		$val=array_shift($args);
		// Get formatting rules
		$conv=localeconv();
		return preg_replace_callback(
			'/\{\s*(?P<pos>\d+)\s*(?:,\s*(?P<type>\w+)\s*'.
			'(?:,\s*(?P<mod>(?:\w+(?:\s*\{.+?\}\s*,?\s*)?)*)'.
			'(?:,\s*(?P<prop>.+?))?)?)?\s*\}/',
			function($expr) use($args,$conv) {
				extract($expr);
				extract($conv);
				if (!array_key_exists($pos,$args))
					return $expr[0];
				if (isset($type)) {
					if (isset($this->hive['FORMATS'][$type]))
						return $this->call($this->hive['FORMATS'][$type],
							[$args[$pos],isset($mod)?$mod:null,isset($prop)?$prop:null]);
					switch ($type) {
						case 'plural':
							preg_match_all('/(?<tag>\w+)'.
								'(?:\s*\{\s*(?<data>.+?)\s*\})/',
								$mod,$matches,PREG_SET_ORDER);
							$ord=['zero','one','two'];
							foreach ($matches as $match) {
								extract($match);
								if (isset($ord[$args[$pos]]) &&
									$tag==$ord[$args[$pos]] || $tag=='other')
									return str_replace('#',$args[$pos],$data);
							}
						case 'number':
							if (isset($mod))
								switch ($mod) {
									case 'integer':
										return number_format(
											$args[$pos],0,'',$thousands_sep);
									case 'currency':
										$int=$cstm=FALSE;
										if (isset($prop) &&
											$cstm=!$int=($prop=='int'))
											$currency_symbol=$prop;
										if (!$cstm &&
											function_exists('money_format'))
											return money_format(
												'%'.($int?'i':'n'),$args[$pos]);
										$fmt=[
											0=>'(nc)',1=>'(n c)',
											2=>'(nc)',10=>'+nc',
											11=>'+n c',12=>'+ nc',
											20=>'nc+',21=>'n c+',
											22=>'nc +',30=>'n+c',
											31=>'n +c',32=>'n+ c',
											40=>'nc+',41=>'n c+',
											42=>'nc +',100=>'(cn)',
											101=>'(c n)',102=>'(cn)',
											110=>'+cn',111=>'+c n',
											112=>'+ cn',120=>'cn+',
											121=>'c n+',122=>'cn +',
											130=>'+cn',131=>'+c n',
											132=>'+ cn',140=>'c+n',
											141=>'c+ n',142=>'c +n'
										];
										if ($args[$pos]<0) {
											$sgn=$negative_sign;
											$pre='n';
										}
										else {
											$sgn=$positive_sign;
											$pre='p';
										}
										return str_replace(
											['+','n','c'],
											[$sgn,number_format(
												abs($args[$pos]),
												$frac_digits,
												$decimal_point,
												$thousands_sep),
												$int?$int_curr_symbol
													:$currency_symbol],
											$fmt[(int)(
												(${$pre.'_cs_precedes'}%2).
												(${$pre.'_sign_posn'}%5).
												(${$pre.'_sep_by_space'}%3)
											)]
										);
									case 'percent':
										return number_format(
											$args[$pos]*100,0,$decimal_point,
											$thousands_sep).'%';
								}
							return number_format(
								$args[$pos],isset($prop)?$prop:2,
								$decimal_point,$thousands_sep);
						case 'date':
							if (empty($mod) || $mod=='short')
								$prop='%x';
							elseif ($mod=='long')
								$prop='%A, %d %B %Y';
							return strftime($prop,$args[$pos]);
						case 'time':
							if (empty($mod) || $mod=='short')
								$prop='%X';
							return strftime($prop,$args[$pos]);
						default:
							return $expr[0];
					}
				}
				return $args[$pos];
			},
			$val
		);
	}

	/**
	*	Assign/auto-detect language
	*	@return string
	*	@param $code string
	**/
	function language($code) {
		$code=preg_replace('/\h+|;q=[0-9.]+/','',$code);
		$code.=($code?',':'').$this->fallback;
		$this->languages=[];
		foreach (array_reverse(explode(',',$code)) as $lang)
			if (preg_match('/^(\w{2})(?:-(\w{2}))?\b/i',$lang,$parts)) {
				// Generic language
				array_unshift($this->languages,$parts[1]);
				if (isset($parts[2])) {
					// Specific language
					$parts[0]=$parts[1].'-'.($parts[2]=strtoupper($parts[2]));
					array_unshift($this->languages,$parts[0]);
				}
			}
		$this->languages=array_unique($this->languages);
		$locales=[];
		$windows=preg_match('/^win/i',PHP_OS);
		// Work around PHP's Turkish locale bug
		foreach (preg_grep('/^(?!tr)/i',$this->languages) as $locale) {
			if ($windows) {
				$parts=explode('-',$locale);
				$locale=@constant('ISO::LC_'.$parts[0]);
				if (isset($parts[1]) &&
					$country=@constant('ISO::CC_'.strtolower($parts[1])))
					$locale.='-'.$country;
			}
			$locale=str_replace('-','_',$locale);
			$locales[]=$locale.'.'.ini_get('default_charset');
			$locales[]=$locale;
		}
		setlocale(LC_ALL,$locales);
		return $this->hive['LANGUAGE']=implode(',',$this->languages);
	}

	/**
	*	Return lexicon entries
	*	@return array
	*	@param $path string
	*	@param $ttl int
	**/
	function lexicon($path,$ttl=0) {
		$languages=$this->languages?:explode(',',$this->fallback);
		$cache=Cache::instance();
		if ($cache->exists(
			$hash=$this->hash(implode(',',$languages)).'.dic',$lex))
			return $lex;
		$lex=[];
		foreach ($languages as $lang)
			foreach ($this->split($path) as $dir)
				if ((is_file($file=($base=$dir.$lang).'.php') ||
					is_file($file=$base.'.php')) &&
					is_array($dict=require($file)))
					$lex+=$dict;
				elseif (is_file($file=$base.'.ini')) {
					preg_match_all(
						'/(?<=^|\n)(?:'.
							'\[(?<prefix>.+?)\]|'.
							'(?<lval>[^\h\r\n;].*?)\h*=\h*'.
							'(?<rval>(?:\\\\\h*\r?\n|.+?)*)'.
						')(?=\r?\n|$)/',
						$this->read($file),$matches,PREG_SET_ORDER);
					if ($matches) {
						$prefix='';
						foreach ($matches as $match)
							if ($match['prefix'])
								$prefix=$match['prefix'].'.';
							elseif (!array_key_exists(
								$key=$prefix.$match['lval'],$lex))
								$lex[$key]=trim(preg_replace(
									'/\\\\\h*\r?\n/',"\n",$match['rval']));
					}
				}
		if ($ttl)
			$cache->set($hash,$lex,$ttl);
		return $lex;
	}

	/**
	*	Return string representation of PHP value
	*	@return string
	*	@param $arg mixed
	**/
	function serialize($arg) {
		switch (strtolower($this->hive['SERIALIZER'])) {
			case 'igbinary':
				return igbinary_serialize($arg);
			default:
				return serialize($arg);
		}
	}

	/**
	*	Return PHP value derived from string
	*	@return string
	*	@param $arg mixed
	**/
	function unserialize($arg) {
		switch (strtolower($this->hive['SERIALIZER'])) {
			case 'igbinary':
				return igbinary_unserialize($arg);
			default:
				return unserialize($arg);
		}
	}

	/**
	*	Send HTTP status header; Return text equivalent of status code
	*	@return string
	*	@param $code int
	**/
	function status($code) {
		$reason=@constant('self::HTTP_'.$code);
		if (!$this->hive['CLI'] && !headers_sent())
			header($_SERVER['SERVER_PROTOCOL'].' '.$code.' '.$reason);
		return $reason;
	}

	/**
	*	Send cache metadata to HTTP client
	*	@return NULL
	*	@param $secs int
	**/
	function expire($secs=0) {
		if (!$this->hive['CLI'] && !headers_sent()) {
			$secs=(int)$secs;
			if ($this->hive['PACKAGE'])
				header('X-Powered-By: '.$this->hive['PACKAGE']);
			if ($this->hive['XFRAME'])
				header('X-Frame-Options: '.$this->hive['XFRAME']);
			header('X-XSS-Protection: 1; mode=block');
			header('X-Content-Type-Options: nosniff');
			if ($this->hive['VERB']=='GET' && $secs) {
				$time=microtime(TRUE);
				header_remove('Pragma');
				header('Cache-Control: max-age='.$secs);
				header('Expires: '.gmdate('r',$time+$secs));
				header('Last-Modified: '.gmdate('r'));
			}
			else {
				header('Pragma: no-cache');
				header('Cache-Control: no-cache, no-store, must-revalidate');
				header('Expires: '.gmdate('r',0));
			}
		}
	}

	/**
	*	Return HTTP user agent
	*	@return string
	**/
	function agent() {
		$headers=$this->hive['HEADERS'];
		return isset($headers['X-Operamini-Phone-UA'])?
			$headers['X-Operamini-Phone-UA']:
			(isset($headers['X-Skyfire-Phone'])?
				$headers['X-Skyfire-Phone']:
				(isset($headers['User-Agent'])?
					$headers['User-Agent']:''));
	}

	/**
	*	Return TRUE if XMLHttpRequest detected
	*	@return bool
	**/
	function ajax() {
		$headers=$this->hive['HEADERS'];
		return isset($headers['X-Requested-With']) &&
			$headers['X-Requested-With']=='XMLHttpRequest';
	}

	/**
	*	Sniff IP address
	*	@return string
	**/
	function ip() {
		$headers=$this->hive['HEADERS'];
		return isset($headers['Client-IP'])?
			$headers['Client-IP']:
			(isset($headers['X-Forwarded-For'])?
				explode(',',$headers['X-Forwarded-For'])[0]:
				(isset($_SERVER['REMOTE_ADDR'])?
					$_SERVER['REMOTE_ADDR']:''));
	}

	/**
	*	Return filtered stack trace as a formatted string (or array)
	*	@return string|array
	*	@param $trace array|NULL
	*	@param $format bool
	**/
	function trace(array $trace=NULL,$format=TRUE) {
		if (!$trace) {
			$trace=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
			$frame=$trace[0];
			if (isset($frame['file']) && $frame['file']==__FILE__)
				array_shift($trace);
		}
		$debug=$this->hive['DEBUG'];
		$trace=array_filter(
			$trace,
			function($frame) use($debug) {
				return isset($frame['file']) &&
					($debug>1 ||
					($frame['file']!=__FILE__ || $debug) &&
					(empty($frame['function']) ||
					!preg_match('/^(?:(?:trigger|user)_error|'.
						'__call|call_user_func)/',$frame['function'])));
			}
		);
		if (!$format)
			return $trace;
		$out='';
		$eol="\n";
		// Analyze stack trace
		foreach ($trace as $frame) {
			$line='';
			if (isset($frame['class']))
				$line.=$frame['class'].$frame['type'];
			if (isset($frame['function']))
				$line.=$frame['function'].'('.
					($debug>2 && isset($frame['args'])?
						$this->csv($frame['args']):'').')';
			$src=$this->fixslashes(str_replace($_SERVER['DOCUMENT_ROOT'].
				'/','',$frame['file'])).':'.$frame['line'];
			$out.='['.$src.'] '.$line.$eol;
		}
		return $out;
	}

	/**
	*	Log error; Execute ONERROR handler if defined, else display
	*	default error page (HTML for synchronous requests, JSON string
	*	for AJAX requests)
	*	@return NULL
	*	@param $code int
	*	@param $text string
	*	@param $trace array
	*	@param $level int
	**/
	function error($code,$text='',array $trace=NULL,$level=0) {
		$prior=$this->hive['ERROR'];
		$header=$this->status($code);
		$req=$this->hive['VERB'].' '.$this->hive['PATH'];
		if ($this->hive['QUERY'])
			$req.='?'.$this->hive['QUERY'];
		if (!$text)
			$text='HTTP '.$code.' ('.$req.')';
		$trace=$this->trace($trace);
		$loggable=$this->hive['LOGGABLE'];
		if (!is_array($loggable))
			$loggable=$this->split($loggable);
		foreach ($loggable as $status)
			if ($status=='*' || preg_match('/^'.preg_replace('/\D/','\d',$status).'$/',$code)) {
				error_log($text);
				foreach (explode("\n",$trace) as $nexus)
					if ($nexus)
						error_log($nexus);
				break;
			}
		if ($highlight=!$this->hive['CLI'] && !$this->hive['AJAX'] &&
			$this->hive['HIGHLIGHT'] && is_file($css=__DIR__.'/'.self::CSS))
			$trace=$this->highlight($trace);
		$this->hive['ERROR']=[
			'status'=>$header,
			'code'=>$code,
			'text'=>$text,
			'trace'=>$trace,
			'level'=>$level
		];
		$this->expire(-1);
		$handler=$this->hive['ONERROR'];
		$this->hive['ONERROR']=NULL;
		$eol="\n";
		if ((!$handler ||
			$this->call($handler,[$this,$this->hive['PARAMS']],
				'beforeroute,afterroute')===FALSE) &&
			!$prior && !$this->hive['CLI'] && !$this->hive['QUIET'])
			echo $this->hive['AJAX']?
				json_encode(array_diff_key($this->hive['ERROR'],$this->hive['DEBUG']?[]:['trace'=>1])):
				('<!DOCTYPE html>'.$eol.
				'<html>'.$eol.
				'<head>'.
					'<title>'.$code.' '.$header.'</title>'.
					($highlight?
						('<style>'.$this->read($css).'</style>'):'').
				'</head>'.$eol.
				'<body>'.$eol.
					'<h1>'.$header.'</h1>'.$eol.
					'<p>'.$this->encode($text?:$req).'</p>'.$eol.
					($this->hive['DEBUG']?('<pre>'.$trace.'</pre>'.$eol):'').
				'</body>'.$eol.
				'</html>');
		if ($this->hive['HALT'])
			die(1);
	}

	/**
	*	Mock HTTP request
	*	@return mixed
	*	@param $pattern string
	*	@param $args array
	*	@param $headers array
	*	@param $body string
	**/
	function mock($pattern,
		array $args=NULL,array $headers=NULL,$body=NULL) {
		if (!$args)
			$args=[];
		$types=['sync','ajax','cli'];
		preg_match('/([\|\w]+)\h+(?:@(\w+)(?:(\(.+?)\))*|([^\h]+))'.
			'(?:\h+\[('.implode('|',$types).')\])?/',$pattern,$parts);
		$verb=strtoupper($parts[1]);
		if ($parts[2]) {
			if (empty($this->hive['ALIASES'][$parts[2]]))
				user_error(sprintf(self::E_Named,$parts[2]),E_USER_ERROR);
			$parts[4]=$this->hive['ALIASES'][$parts[2]];
			$parts[4]=$this->build($parts[4],
				isset($parts[3])?$this->parse($parts[3]):[]);
		}
		if (empty($parts[4]))
			user_error(sprintf(self::E_Pattern,$pattern),E_USER_ERROR);
		$url=parse_url($parts[4]);
		parse_str(@$url['query'],$GLOBALS['_GET']);
		if (preg_match('/GET|HEAD/',$verb))
			$GLOBALS['_GET']=array_merge($GLOBALS['_GET'],$args);
		$GLOBALS['_POST']=$verb=='POST'?$args:[];
		$GLOBALS['_REQUEST']=array_merge($GLOBALS['_GET'],$GLOBALS['_POST']);
		foreach ($headers?:[] as $key=>$val)
			$_SERVER['HTTP_'.strtr(strtoupper($key),'-','_')]=$val;
		$this->hive['VERB']=$verb;
		$this->hive['PATH']=$url['path'];
		$this->hive['URI']=$this->hive['BASE'].$url['path'];
		if ($GLOBALS['_GET'])
			$this->hive['URI'].='?'.http_build_query($GLOBALS['_GET']);
		$this->hive['BODY']='';
		if (!preg_match('/GET|HEAD/',$verb))
			$this->hive['BODY']=$body?:http_build_query($args);
		$this->hive['AJAX']=isset($parts[5]) &&
			preg_match('/ajax/i',$parts[5]);
		$this->hive['CLI']=isset($parts[5]) &&
			preg_match('/cli/i',$parts[5]);
		return $this->run();
	}

	/**
	*	Assemble url from alias name
	*	@return string
	*	@param $name string
	*	@param $params array|string
	*	@param $query string|array
	**/
	function alias($name,$params=[],$query=NULL) {
		if (!is_array($params))
			$params=$this->parse($params);
		if (empty($this->hive['ALIASES'][$name]))
			user_error(sprintf(self::E_Named,$name),E_USER_ERROR);
		$url=$this->build($this->hive['ALIASES'][$name],$params);
		if (is_array($query))
			$query=http_build_query($query);
		return $url.($query?('?'.$query):'');
	}

	/**
	*	Bind handler to route pattern
	*	@return NULL
	*	@param $pattern string|array
	*	@param $handler callback
	*	@param $ttl int
	*	@param $kbps int
	**/
	function route($pattern,$handler,$ttl=0,$kbps=0) {
		$types=['sync','ajax','cli'];
		$alias=null;
		if (is_array($pattern)) {
			foreach ($pattern as $item)
				$this->route($item,$handler,$ttl,$kbps);
			return;
		}
		preg_match('/([\|\w]+)\h+(?:(?:@?(.+?)\h*:\h*)?(@(\w+)|[^\h]+))'.
			'(?:\h+\[('.implode('|',$types).')\])?/u',$pattern,$parts);
		if (isset($parts[2]) && $parts[2])
			$this->hive['ALIASES'][$alias=$parts[2]]=$parts[3];
		elseif (!empty($parts[4])) {
			if (empty($this->hive['ALIASES'][$parts[4]]))
				user_error(sprintf(self::E_Named,$parts[4]),E_USER_ERROR);
			$parts[3]=$this->hive['ALIASES'][$alias=$parts[4]];
		}
		if (empty($parts[3]))
			user_error(sprintf(self::E_Pattern,$pattern),E_USER_ERROR);
		$type=empty($parts[5])?0:constant('self::REQ_'.strtoupper($parts[5]));
		foreach ($this->split($parts[1]) as $verb) {
			if (!preg_match('/'.self::VERBS.'/',$verb))
				$this->error(501,$verb.' '.$this->hive['URI']);
			$this->hive['ROUTES'][$parts[3]][$type][strtoupper($verb)]=
				[$handler,$ttl,$kbps,$alias];
		}
	}

	/**
	*	Reroute to specified URI
	*	@return NULL
	*	@param $url array|string
	*	@param $permanent bool
	*	@param $die bool
	**/
	function reroute($url=NULL,$permanent=FALSE,$die=TRUE) {
		if (!$url)
			$url=$this->hive['REALM'];
		if (is_array($url))
			$url=call_user_func_array([$this,'alias'],$url);
		elseif (preg_match('/^(?:@?([^\/()?]+)(?:(\(.+?)\))*(\?.+)*)/',
			$url,$parts) &&
			isset($this->hive['ALIASES'][$parts[1]]))
			$url=$this->hive['ALIASES'][$parts[1]];
		$url=$this->build($url,isset($parts[2])?$this->parse($parts[2]):[]).
			(isset($parts[3])?$parts[3]:'');
		if (($handler=$this->hive['ONREROUTE']) &&
			$this->call($handler,[$url,$permanent])!==FALSE)
			return;
		if ($url[0]=='/' && (empty($url[1]) || $url[1]!='/')) {
			$port=$this->hive['PORT'];
			$port=in_array($port,[80,443])?'':(':'.$port);
			$url=$this->hive['SCHEME'].'://'.
				$this->hive['HOST'].$port.$this->hive['BASE'].$url;
		}
		if ($this->hive['CLI'])
			$this->mock('GET '.$url.' [cli]');
		else {
			header('Location: '.$url);
			$this->status($permanent?301:302);
			if ($die)
				die;
		}
	}

	/**
	*	Provide ReST interface by mapping HTTP verb to class method
	*	@return NULL
	*	@param $url string
	*	@param $class string|object
	*	@param $ttl int
	*	@param $kbps int
	**/
	function map($url,$class,$ttl=0,$kbps=0) {
		if (is_array($url)) {
			foreach ($url as $item)
				$this->map($item,$class,$ttl,$kbps);
			return;
		}
		foreach (explode('|',self::VERBS) as $method)
			$this->route($method.' '.$url,is_string($class)?
				$class.'->'.$this->hive['PREMAP'].strtolower($method):
				[$class,$this->hive['PREMAP'].strtolower($method)],
				$ttl,$kbps);
	}

	/**
	*	Redirect a route to another URL
	*	@return NULL
	*	@param $pattern string|array
	*	@param $url string
	*	@param $permanent bool
	*/
	function redirect($pattern,$url,$permanent=TRUE) {
		if (is_array($pattern)) {
			foreach ($pattern as $item)
				$this->redirect($item,$url,$permanent);
			return;
		}
		$this->route($pattern,function($fw) use($url,$permanent) {
			$fw->reroute($url,$permanent);
		});
	}

	/**
	*	Return TRUE if IPv4 address exists in DNSBL
	*	@return bool
	*	@param $ip string
	**/
	function blacklisted($ip) {
		if ($this->hive['DNSBL'] &&
			!in_array($ip,
				is_array($this->hive['EXEMPT'])?
					$this->hive['EXEMPT']:
					$this->split($this->hive['EXEMPT']))) {
			// Reverse IPv4 dotted quad
			$rev=implode('.',array_reverse(explode('.',$ip)));
			foreach (is_array($this->hive['DNSBL'])?
				$this->hive['DNSBL']:
				$this->split($this->hive['DNSBL']) as $server)
				// DNSBL lookup
				if (checkdnsrr($rev.'.'.$server,'A'))
					return TRUE;
		}
		return FALSE;
	}

	/**
	*	Applies the specified URL mask and returns parameterized matches
	*	@return $args array
	*	@param $pattern string
	*	@param $url string|NULL
	**/
	function mask($pattern,$url=NULL) {
		if (!$url)
			$url=$this->rel($this->hive['URI']);
		$case=$this->hive['CASELESS']?'i':'';
		$wild=preg_quote($pattern,'/');
		$i=0;
		while (is_int($pos=strpos($wild,'\*'))) {
			$wild=substr_replace($wild,'(?P<_'.$i.'>[^\?]*)',$pos,2);
			$i++;
		}
		preg_match('/^'.
			preg_replace(
				'/((\\\{)?@(\w+\b)(?(2)\\\}))/',
				'(?P<\3>[^\/\?]+)',
				$wild).'\/?$/'.$case.'um',$url,$args);
		foreach (array_keys($args) as $key) {
			if (preg_match('/^_\d+$/',$key)) {
				if (empty($args['*']))
					$args['*']=$args[$key];
				else {
					if (is_string($args['*']))
						$args['*']=[$args['*']];
					array_push($args['*'],$args[$key]);
				}
				unset($args[$key]);
			}
			elseif (is_numeric($key) && $key)
				unset($args[$key]);
		}
		return $args;
	}

	/**
	*	Match routes against incoming URI
	*	@return mixed
	**/
	function run() {
		if ($this->blacklisted($this->hive['IP']))
			// Spammer detected
			$this->error(403);
		if (!$this->hive['ROUTES'])
			// No routes defined
			user_error(self::E_Routes,E_USER_ERROR);
		// Match specific routes first
		$paths=[];
		foreach ($keys=array_keys($this->hive['ROUTES']) as $key) {
			$path=preg_replace('/@\w+/','*@',$key);
			if (substr($path,-1)!='*')
				$path.='+';
			$paths[]=$path;
		}
		$vals=array_values($this->hive['ROUTES']);
		array_multisort($paths,SORT_DESC,$keys,$vals);
		$this->hive['ROUTES']=array_combine($keys,$vals);
		// Convert to BASE-relative URL
		$req=urldecode($this->hive['PATH']);
		$preflight=FALSE;
		if ($cors=(isset($this->hive['HEADERS']['Origin']) &&
			$this->hive['CORS']['origin'])) {
			$cors=$this->hive['CORS'];
			header('Access-Control-Allow-Origin: '.$cors['origin']);
			header('Access-Control-Allow-Credentials: '.
				var_export($cors['credentials'],TRUE));
			$preflight=
				isset($this->hive['HEADERS']['Access-Control-Request-Method']);
		}
		$allowed=[];
		foreach ($this->hive['ROUTES'] as $pattern=>$routes) {
			if (!$args=$this->mask($pattern,$req))
				continue;
			ksort($args);
			$route=NULL;
			$ptr=$this->hive['CLI']?self::REQ_CLI:$this->hive['AJAX']+1;
			if (isset($routes[$ptr][$this->hive['VERB']]) ||
				isset($routes[$ptr=0]))
				$route=$routes[$ptr];
			if (!$route)
				continue;
			if (isset($route[$this->hive['VERB']]) && !$preflight) {
				if ($this->hive['VERB']=='GET' &&
					preg_match('/.+\/$/',$this->hive['PATH']))
					$this->reroute(substr($this->hive['PATH'],0,-1).
						($this->hive['QUERY']?('?'.$this->hive['QUERY']):''));
				list($handler,$ttl,$kbps,$alias)=$route[$this->hive['VERB']];
				// Capture values of route pattern tokens
				$this->hive['PARAMS']=$args;
				// Save matching route
				$this->hive['ALIAS']=$alias;
				$this->hive['PATTERN']=$pattern;
				if ($cors && $cors['expose'])
					header('Access-Control-Expose-Headers: '.
						(is_array($cors['expose'])?
							implode(',',$cors['expose']):$cors['expose']));
				if (is_string($handler)) {
					// Replace route pattern tokens in handler if any
					$handler=preg_replace_callback('/({)?@(\w+\b)(?(1)})/',
						function($id) use($args) {
							$pid=count($id)>2?2:1;
							return isset($args[$id[$pid]])?
								$args[$id[$pid]]:
								$id[0];
						},
						$handler
					);
					if (preg_match('/(.+)\h*(?:->|::)/',$handler,$match) &&
						!class_exists($match[1]))
						$this->error(404);
				}
				// Process request
				$result=NULL;
				$body='';
				$now=microtime(TRUE);
				if (preg_match('/GET|HEAD/',$this->hive['VERB']) && $ttl) {
					// Only GET and HEAD requests are cacheable
					$headers=$this->hive['HEADERS'];
					$cache=Cache::instance();
					$cached=$cache->exists(
						$hash=$this->hash($this->hive['VERB'].' '.
							$this->hive['URI']).'.url',$data);
					if ($cached) {
						if (isset($headers['If-Modified-Since']) &&
							strtotime($headers['If-Modified-Since'])+
								$ttl>$now) {
							$this->status(304);
							die;
						}
						// Retrieve from cache backend
						list($headers,$body,$result)=$data;
						if (!$this->hive['CLI'])
							array_walk($headers,'header');
						$this->expire($cached[0]+$ttl-$now);
					}
					else
						// Expire HTTP client-cached page
						$this->expire($ttl);
				}
				else
					$this->expire(0);
				if (!strlen($body)) {
					if (!$this->hive['RAW'] && !$this->hive['BODY'])
						$this->hive['BODY']=file_get_contents('php://input');
					ob_start();
					// Call route handler
					$result=$this->call($handler,[$this,$args,$handler],
						'beforeroute,afterroute');
					$body=ob_get_clean();
					if (isset($cache) && !error_get_last()) {
						// Save to cache backend
						$cache->set($hash,[
							// Remove cookies
							preg_grep('/Set-Cookie\:/',headers_list(),
								PREG_GREP_INVERT),$body,$result],$ttl);
					}
				}
				$this->hive['RESPONSE']=$body;
				if (!$this->hive['QUIET']) {
					if ($kbps) {
						$ctr=0;
						foreach (str_split($body,1024) as $part) {
							// Throttle output
							$ctr++;
							if ($ctr/$kbps>($elapsed=microtime(TRUE)-$now) &&
								!connection_aborted())
								usleep(1e6*($ctr/$kbps-$elapsed));
							echo $part;
						}
					}
					else
						echo $body;
				}
				if ($result || $this->hive['VERB']!='OPTIONS')
					return $result;
			}
			$allowed=array_merge($allowed,array_keys($route));
		}
		if (!$allowed)
			// URL doesn't match any route
			$this->error(404);
		elseif (!$this->hive['CLI']) {
			// Unhandled HTTP method
			header('Allow: '.implode(',',array_unique($allowed)));
			if ($cors) {
				header('Access-Control-Allow-Methods: OPTIONS,'.
					implode(',',$allowed));
				if ($cors['headers'])
					header('Access-Control-Allow-Headers: '.
						(is_array($cors['headers'])?
							implode(',',$cors['headers']):
							$cors['headers']));
				if ($cors['ttl']>0)
					header('Access-Control-Max-Age: '.$cors['ttl']);
			}
			if ($this->hive['VERB']!='OPTIONS')
				$this->error(405);
		}
		return FALSE;
	}

	/**
	*	Loop until callback returns TRUE (for long polling)
	*	@return mixed
	*	@param $func callback
	*	@param $args array
	*	@param $timeout int
	**/
	function until($func,$args=NULL,$timeout=60) {
		if (!$args)
			$args=[];
		$time=time();
		$max=ini_get('max_execution_time');
		$limit=max(0,($max?min($timeout,$max):$timeout)-1);
		$out='';
		// Turn output buffering on
		ob_start();
		// Not for the weak of heart
		while (
			// No error occurred
			!$this->hive['ERROR'] &&
			// Got time left?
			time()-$time+1<$limit &&
			// Still alive?
			!connection_aborted() &&
			// Restart session
			!headers_sent() &&
			(session_status()==PHP_SESSION_ACTIVE || session_start()) &&
			// CAUTION: Callback will kill host if it never becomes truthy!
			!$out=$this->call($func,$args)) {
			if (!$this->hive['CLI'])
				session_commit();
			// Hush down
			sleep(1);
		}
		ob_flush();
		flush();
		return $out;
	}

	/**
	*	Disconnect HTTP client
	**/
	function abort() {
		if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
			session_start();
		$out='';
		while (ob_get_level())
			$out=ob_get_clean().$out;
		header('Content-Encoding: none');
		header('Content-Length: '.strlen($out));
		header('Connection: close');
		session_commit();
		echo $out;
		flush();
		if (function_exists('fastcgi_finish_request'))
			fastcgi_finish_request();
	}

	/**
	*	Grab the real route handler behind the string expression
	*	@return string|array
	*	@param $func string
	*	@param $args array
	**/
	function grab($func,$args=NULL) {
		if (preg_match('/(.+)\h*(->|::)\h*(.+)/s',$func,$parts)) {
			// Convert string to executable PHP callback
			if (!class_exists($parts[1]))
				user_error(sprintf(self::E_Class,$parts[1]),E_USER_ERROR);
			if ($parts[2]=='->') {
				if (is_subclass_of($parts[1],'Prefab'))
					$parts[1]=call_user_func($parts[1].'::instance');
				elseif ($container=$this->get('CONTAINER')) {
					if (is_object($container) && is_callable([$container,'has'])
						&& $container->has($parts[1])) // PSR11
						$parts[1]=call_user_func([$container,'get'],$parts[1]);
					elseif (is_callable($container))
						$parts[1]=call_user_func($container,$parts[1],$args);
					elseif (is_string($container) && is_subclass_of($container,'Prefab'))
						$parts[1]=call_user_func($container.'::instance')->get($parts[1]);
					else
						user_error(sprintf(self::E_Class,
							$this->stringify($container)),
							E_USER_ERROR);
				}
				else {
					$ref=new ReflectionClass($parts[1]);
					$parts[1]=method_exists($parts[1],'__construct') && $args?
						$ref->newinstanceargs($args):
						$ref->newinstance();
				}
			}
			$func=[$parts[1],$parts[3]];
		}
		return $func;
	}

	/**
	*	Execute callback/hooks (supports 'class->method' format)
	*	@return mixed|FALSE
	*	@param $func callback
	*	@param $args mixed
	*	@param $hooks string
	**/
	function call($func,$args=NULL,$hooks='') {
		if (!is_array($args))
			$args=[$args];
		// Grab the real handler behind the string representation
		if (is_string($func))
			$func=$this->grab($func,$args);
		// Execute function; abort if callback/hook returns FALSE
		if (!is_callable($func))
			// No route handler
			if ($hooks=='beforeroute,afterroute') {
				$allowed=[];
				if (is_array($func))
					$allowed=array_intersect(
						array_map('strtoupper',get_class_methods($func[0])),
						explode('|',self::VERBS)
					);
				header('Allow: '.implode(',',$allowed));
				$this->error(405);
			}
			else
				user_error(sprintf(self::E_Method,
					is_string($func)?$func:$this->stringify($func)),
					E_USER_ERROR);
		$obj=FALSE;
		if (is_array($func)) {
			$hooks=$this->split($hooks);
			$obj=TRUE;
		}
		// Execute pre-route hook if any
		if ($obj && $hooks && in_array($hook='beforeroute',$hooks) &&
			method_exists($func[0],$hook) &&
			call_user_func_array([$func[0],$hook],$args)===FALSE)
			return FALSE;
		// Execute callback
		$out=call_user_func_array($func,$args?:[]);
		if ($out===FALSE)
			return FALSE;
		// Execute post-route hook if any
		if ($obj && $hooks && in_array($hook='afterroute',$hooks) &&
			method_exists($func[0],$hook) &&
			call_user_func_array([$func[0],$hook],$args)===FALSE)
			return FALSE;
		return $out;
	}

	/**
	*	Execute specified callbacks in succession; Apply same arguments
	*	to all callbacks
	*	@return array
	*	@param $funcs array|string
	*	@param $args mixed
	**/
	function chain($funcs,$args=NULL) {
		$out=[];
		foreach (is_array($funcs)?$funcs:$this->split($funcs) as $func)
			$out[]=$this->call($func,$args);
		return $out;
	}

	/**
	*	Execute specified callbacks in succession; Relay result of
	*	previous callback as argument to the next callback
	*	@return array
	*	@param $funcs array|string
	*	@param $args mixed
	**/
	function relay($funcs,$args=NULL) {
		foreach (is_array($funcs)?$funcs:$this->split($funcs) as $func)
			$args=[$this->call($func,$args)];
		return array_shift($args);
	}

	/**
	*	Configure framework according to .ini-style file settings;
	*	If optional 2nd arg is provided, template strings are interpreted
	*	@return object
	*	@param $source string|array
	*	@param $allow bool
	**/
	function config($source,$allow=FALSE) {
		if (is_string($source))
			$source=$this->split($source);
		if ($allow)
			$preview=Preview::instance();
		foreach ($source as $file) {
			preg_match_all(
				'/(?<=^|\n)(?:'.
					'\[(?<section>.+?)\]|'.
					'(?<lval>[^\h\r\n;].*?)\h*=\h*'.
					'(?<rval>(?:\\\\\h*\r?\n|.+?)*)'.
				')(?=\r?\n|$)/',
				$this->read($file),
				$matches,PREG_SET_ORDER);
			if ($matches) {
				$sec='globals';
				$cmd=[];
				foreach ($matches as $match) {
					if ($match['section']) {
						$sec=$match['section'];
						if (preg_match(
							'/^(?!(?:global|config|route|map|redirect)s\b)'.
							'((?:\.?\w)+)/i',$sec,$msec) &&
							!$this->exists($msec[0]))
							$this->set($msec[0],NULL);
						preg_match('/^(config|route|map|redirect)s\b|'.
							'^((?:\.?\w)+)\s*\>\s*(.*)/i',$sec,$cmd);
						continue;
					}
					if ($allow)
						foreach (['lval','rval'] as $ndx)
							$match[$ndx]=$preview->
								resolve($match[$ndx],NULL,0,FALSE,FALSE);
					if (!empty($cmd)) {
						isset($cmd[3])?
						$this->call($cmd[3],
							[$match['lval'],$match['rval'],$cmd[2]]):
						call_user_func_array(
							[$this,$cmd[1]],
							array_merge([$match['lval']],
								str_getcsv($cmd[1]=='config'?$this->cast($match['rval']):
									$match['rval']))
						);
					}
					else {
						$rval=preg_replace(
							'/\\\\\h*(\r?\n)/','\1',$match['rval']);
						$ttl=NULL;
						if (preg_match('/^(.+)\|\h*(\d+)$/',$rval,$tmp)) {
							array_shift($tmp);
							list($rval,$ttl)=$tmp;
						}
						$args=array_map(
							function($val) {
								$val=$this->cast($val);
								return is_string($val)
									? preg_replace('/\\\\"/','"',$val)
									: $val;
							},
							// Mark quoted strings with 0x00 whitespace
							str_getcsv(preg_replace(
								'/(?<!\\\\)(")(.*?)\1/',
								"\\1\x00\\2\\1",trim($rval)))
						);
						preg_match('/^(?<section>[^:]+)(?:\:(?<func>.+))?/',
							$sec,$parts);
						$func=isset($parts['func'])?$parts['func']:NULL;
						$custom=(strtolower($parts['section'])!='globals');
						if ($func)
							$args=[$this->call($func,$args)];
						if (count($args)>1)
							$args=[$args];
						if (isset($ttl))
							$args=array_merge($args,[$ttl]);
						call_user_func_array(
							[$this,'set'],
							array_merge(
								[
									($custom?($parts['section'].'.'):'').
									$match['lval']
								],
								$args
							)
						);
					}
				}
			}
		}
		return $this;
	}

	/**
	*	Create mutex, invoke callback then drop ownership when done
	*	@return mixed
	*	@param $id string
	*	@param $func callback
	*	@param $args mixed
	**/
	function mutex($id,$func,$args=NULL) {
		if (!is_dir($tmp=$this->hive['TEMP']))
			mkdir($tmp,self::MODE,TRUE);
		// Use filesystem lock
		if (is_file($lock=$tmp.
			$this->get('SEED').'.'.$this->hash($id).'.lock') &&
			filemtime($lock)+ini_get('max_execution_time')<microtime(TRUE))
			// Stale lock
			@unlink($lock);
		while (!($handle=@fopen($lock,'x')) && !connection_aborted())
			usleep(mt_rand(0,100));
		$this->locks[$id]=$lock;
		$out=$this->call($func,$args);
		fclose($handle);
		@unlink($lock);
		unset($this->locks[$id]);
		return $out;
	}

	/**
	*	Read file (with option to apply Unix LF as standard line ending)
	*	@return string
	*	@param $file string
	*	@param $lf bool
	**/
	function read($file,$lf=FALSE) {
		$out=@file_get_contents($file);
		return $lf?preg_replace('/\r\n|\r/',"\n",$out):$out;
	}

	/**
	*	Exclusive file write
	*	@return int|FALSE
	*	@param $file string
	*	@param $data mixed
	*	@param $append bool
	**/
	function write($file,$data,$append=FALSE) {
		return file_put_contents($file,$data,LOCK_EX|($append?FILE_APPEND:0));
	}

	/**
	*	Apply syntax highlighting
	*	@return string
	*	@param $text string
	**/
	function highlight($text) {
		$out='';
		$pre=FALSE;
		$text=trim($text);
		if ($text && !preg_match('/^<\?php/',$text)) {
			$text='<?php '.$text;
			$pre=TRUE;
		}
		foreach (token_get_all($text) as $token)
			if ($pre)
				$pre=FALSE;
			else
				$out.='<span'.
					(is_array($token)?
						(' class="'.
							substr(strtolower(token_name($token[0])),2).'">'.
							$this->encode($token[1]).''):
						('>'.$this->encode($token))).
					'</span>';
		return $out?('<code>'.$out.'</code>'):$text;
	}

	/**
	*	Dump expression with syntax highlighting
	*	@return NULL
	*	@param $expr mixed
	**/
	function dump($expr) {
		echo $this->highlight($this->stringify($expr));
	}

	/**
	*	Return path (and query parameters) relative to the base directory
	*	@return string
	*	@param $url string
	**/
	function rel($url) {
		return preg_replace('/^(?:https?:\/\/)?'.
			preg_quote($this->hive['BASE'],'/').'(\/.*|$)/','\1',$url);
	}

	/**
	*	Namespace-aware class autoloader
	*	@return mixed
	*	@param $class string
	**/
	protected function autoload($class) {
		$class=$this->fixslashes(ltrim($class,'\\'));
		$func=NULL;
		if (is_array($path=$this->hive['AUTOLOAD']) &&
			isset($path[1]) && is_callable($path[1]))
			list($path,$func)=$path;
		foreach ($this->split($this->hive['PLUGINS'].';'.$path) as $auto)
			if ($func && is_file($file=$func($auto.$class).'.php') ||
				is_file($file=$auto.$class.'.php') ||
				is_file($file=$auto.strtolower($class).'.php') ||
				is_file($file=strtolower($auto.$class).'.php'))
				return require($file);
	}

	/**
	*	Execute framework/application shutdown sequence
	*	@param $cwd string
	**/
	function unload($cwd) {
		chdir($cwd);
		if (!($error=error_get_last()) &&
			session_status()==PHP_SESSION_ACTIVE)
			session_commit();
		foreach ($this->locks as $lock)
			@unlink($lock);
		$handler=$this->hive['UNLOAD'];
		if ((!$handler || $this->call($handler,$this)===FALSE) &&
			$error && in_array($error['type'],
			[E_ERROR,E_PARSE,E_CORE_ERROR,E_COMPILE_ERROR]))
			// Fatal error detected
			$this->error(500,
				sprintf(self::E_Fatal,$error['message']),[$error]);
	}

	/**
	*	Convenience method for checking hive key
	*	@return mixed
	*	@param $key string
	**/
	function offsetexists($key) {
		return $this->exists($key);
	}

	/**
	*	Convenience method for assigning hive value
	*	@return mixed
	*	@param $key string
	*	@param $val scalar
	**/
	function offsetset($key,$val) {
		return $this->set($key,$val);
	}

	/**
	*	Convenience method for retrieving hive value
	*	@return mixed
	*	@param $key string
	**/
	function &offsetget($key) {
		$val=&$this->ref($key);
		return $val;
	}

	/**
	*	Convenience method for removing hive key
	*	@return NULL
	*	@param $key string
	**/
	function offsetunset($key) {
		$this->clear($key);
	}

	/**
	*	Alias for offsetexists()
	*	@return mixed
	*	@param $key string
	**/
	function __isset($key) {
		return $this->offsetexists($key);
	}

	/**
	*	Alias for offsetset()
	*	@return mixed
	*	@param $key string
	*	@param $val mixed
	**/
	function __set($key,$val) {
		return $this->offsetset($key,$val);
	}

	/**
	*	Alias for offsetget()
	*	@return mixed
	*	@param $key string
	**/
	function &__get($key) {
		$val=&$this->offsetget($key);
		return $val;
	}

	/**
	*	Alias for offsetunset()
	*	@return mixed
	*	@param $key string
	**/
	function __unset($key) {
		$this->offsetunset($key);
	}

	/**
	*	Call function identified by hive key
	*	@return mixed
	*	@param $key string
	*	@param $args array
	**/
	function __call($key,array $args) {
		if ($this->exists($key,$val))
			return call_user_func_array($val,$args);
		user_error(sprintf(self::E_Method,$key),E_USER_ERROR);
	}

	//! Prohibit cloning
	private function __clone() {
	}

	//! Bootstrap
	function __construct() {
		// Managed directives
		ini_set('default_charset',$charset='UTF-8');
		if (extension_loaded('mbstring'))
			mb_internal_encoding($charset);
		ini_set('display_errors',0);
		// Deprecated directives
		@ini_set('magic_quotes_gpc',0);
		@ini_set('register_globals',0);
		// Intercept errors/exceptions; PHP5.3-compatible
		$check=error_reporting((E_ALL|E_STRICT)&~(E_NOTICE|E_USER_NOTICE));
		set_exception_handler(
			function($obj) {
				$this->hive['EXCEPTION']=$obj;
				$this->error(500,
					$obj->getmessage().' '.
					'['.$obj->getFile().':'.$obj->getLine().']',
					$obj->gettrace());
			}
		);
		set_error_handler(
			function($level,$text,$file,$line) {
				if ($level & error_reporting())
					$this->error(500,$text,NULL,$level);
			}
		);
		if (!isset($_SERVER['SERVER_NAME']) || $_SERVER['SERVER_NAME']==='')
			$_SERVER['SERVER_NAME']=gethostname();
		if ($cli=PHP_SAPI=='cli') {
			// Emulate HTTP request
			$_SERVER['REQUEST_METHOD']='GET';
			if (!isset($_SERVER['argv'][1])) {
				$_SERVER['argc']++;
				$_SERVER['argv'][1]='/';
			}
			$req=$query='';
			if (substr($_SERVER['argv'][1],0,1)=='/') {
				$req=$_SERVER['argv'][1];
				$query=parse_url($req,PHP_URL_QUERY);
			} else {
				foreach($_SERVER['argv'] as $i=>$arg) {
					if (!$i) continue;
					if (preg_match('/^\-(\-)?(\w+)(?:\=(.*))?$/',$arg,$m)) {
						foreach($m[1]?[$m[2]]:str_split($m[2]) as $k)
							$query.=($query?'&':'').urlencode($k).'=';
						if (isset($m[3]))
							$query.=urlencode($m[3]);
					} else
						$req.='/'.$arg;
				}
				if (!$req)
					$req='/';
				if ($query)
					$req.='?'.$query;
			}
			$_SERVER['REQUEST_URI']=$req;
			parse_str($query,$GLOBALS['_GET']);
		}
		$headers=[];
		if (!$cli) {
			if (function_exists('getallheaders')) {
				foreach (getallheaders() as $key=>$val) {
					$tmp=strtoupper(strtr($key,'-','_'));
					// TODO: use ucwords delimiters for php 5.4.32+ & 5.5.16+
					$key=strtr(ucwords(strtolower(strtr($key,'-',' '))),' ','-');
					$headers[$key]=$val;
					if (isset($_SERVER['HTTP_'.$tmp]))
						$headers[$key]=&$_SERVER['HTTP_'.$tmp];
				}
			}
			else {
				if (isset($_SERVER['CONTENT_LENGTH']))
					$headers['Content-Length']=&$_SERVER['CONTENT_LENGTH'];
				if (isset($_SERVER['CONTENT_TYPE']))
					$headers['Content-Type']=&$_SERVER['CONTENT_TYPE'];
				foreach (array_keys($_SERVER) as $key)
					if (substr($key,0,5)=='HTTP_')
						$headers[strtr(ucwords(strtolower(strtr(
							substr($key,5),'_',' '))),' ','-')]=&$_SERVER[$key];
			}
		}
		if (isset($headers['X-HTTP-Method-Override']))
			$_SERVER['REQUEST_METHOD']=$headers['X-HTTP-Method-Override'];
		elseif ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['_method']))
			$_SERVER['REQUEST_METHOD']=$_POST['_method'];
		$scheme=isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on' ||
			isset($headers['X-Forwarded-Proto']) &&
			$headers['X-Forwarded-Proto']=='https'?'https':'http';
		// Create hive early on to expose header methods
		$this->hive=['HEADERS'=>&$headers];
		if (function_exists('apache_setenv')) {
			// Work around Apache pre-2.4 VirtualDocumentRoot bug
			$_SERVER['DOCUMENT_ROOT']=str_replace($_SERVER['SCRIPT_NAME'],'',
				$_SERVER['SCRIPT_FILENAME']);
			apache_setenv("DOCUMENT_ROOT",$_SERVER['DOCUMENT_ROOT']);
		}
		$_SERVER['DOCUMENT_ROOT']=realpath($_SERVER['DOCUMENT_ROOT']);
		$base='';
		if (!$cli)
			$base=rtrim($this->fixslashes(
				dirname($_SERVER['SCRIPT_NAME'])),'/');
		$uri=parse_url((preg_match('/^\w+:\/\//',$_SERVER['REQUEST_URI'])?'':
				$scheme.'://'.$_SERVER['SERVER_NAME']).$_SERVER['REQUEST_URI']);
		$_SERVER['REQUEST_URI']=$uri['path'].
			(isset($uri['query'])?'?'.$uri['query']:'').
			(isset($uri['fragment'])?'#'.$uri['fragment']:'');
		$path=preg_replace('/^'.preg_quote($base,'/').'/','',$uri['path']);
		$jar=[
			'expire'=>0,
			'lifetime'=>0,
			'path'=>$base?:'/',
			'domain'=>is_int(strpos($_SERVER['SERVER_NAME'],'.')) &&
				!filter_var($_SERVER['SERVER_NAME'],FILTER_VALIDATE_IP)?
				$_SERVER['SERVER_NAME']:'',
			'secure'=>($scheme=='https'),
			'httponly'=>TRUE
		];
		$port=80;
		if (isset($headers['X-Forwarded-Port']))
			$port=$headers['X-Forwarded-Port'];
		elseif (isset($_SERVER['SERVER_PORT']))
			$port=$_SERVER['SERVER_PORT'];
		// Default configuration
		$this->hive+=[
			'AGENT'=>$this->agent(),
			'AJAX'=>$this->ajax(),
			'ALIAS'=>NULL,
			'ALIASES'=>[],
			'AUTOLOAD'=>'./',
			'BASE'=>$base,
			'BITMASK'=>ENT_COMPAT,
			'BODY'=>NULL,
			'CACHE'=>FALSE,
			'CASELESS'=>TRUE,
			'CLI'=>$cli,
			'CORS'=>[
				'headers'=>'',
				'origin'=>FALSE,
				'credentials'=>FALSE,
				'expose'=>FALSE,
				'ttl'=>0
			],
			'DEBUG'=>0,
			'DIACRITICS'=>[],
			'DNSBL'=>'',
			'EMOJI'=>[],
			'ENCODING'=>$charset,
			'ERROR'=>NULL,
			'ESCAPE'=>TRUE,
			'EXCEPTION'=>NULL,
			'EXEMPT'=>NULL,
			'FALLBACK'=>$this->fallback,
			'FORMATS'=>[],
			'FRAGMENT'=>isset($uri['fragment'])?$uri['fragment']:'',
			'HALT'=>TRUE,
			'HIGHLIGHT'=>FALSE,
			'HOST'=>$_SERVER['SERVER_NAME'],
			'IP'=>$this->ip(),
			'JAR'=>$jar,
			'LANGUAGE'=>isset($headers['Accept-Language'])?
				$this->language($headers['Accept-Language']):
				$this->fallback,
			'LOCALES'=>'./',
			'LOGGABLE'=>'*',
			'LOGS'=>'./',
			'MB'=>extension_loaded('mbstring'),
			'ONERROR'=>NULL,
			'ONREROUTE'=>NULL,
			'PACKAGE'=>self::PACKAGE,
			'PARAMS'=>[],
			'PATH'=>$path,
			'PATTERN'=>NULL,
			'PLUGINS'=>$this->fixslashes(__DIR__).'/',
			'PORT'=>$port,
			'PREFIX'=>NULL,
			'PREMAP'=>'',
			'QUERY'=>isset($uri['query'])?$uri['query']:'',
			'QUIET'=>FALSE,
			'RAW'=>FALSE,
			'REALM'=>$scheme.'://'.$_SERVER['SERVER_NAME'].
				($port && !in_array($port,[80,443])?(':'.$port):'').
				$_SERVER['REQUEST_URI'],
			'RESPONSE'=>'',
			'ROOT'=>$_SERVER['DOCUMENT_ROOT'],
			'ROUTES'=>[],
			'SCHEME'=>$scheme,
			'SEED'=>$this->hash($_SERVER['SERVER_NAME'].$base),
			'SERIALIZER'=>extension_loaded($ext='igbinary')?$ext:'php',
			'TEMP'=>'tmp/',
			'TIME'=>&$_SERVER['REQUEST_TIME_FLOAT'],
			'TZ'=>@date_default_timezone_get(),
			'UI'=>'./',
			'UNLOAD'=>NULL,
			'UPLOADS'=>'./',
			'URI'=>&$_SERVER['REQUEST_URI'],
			'VERB'=>&$_SERVER['REQUEST_METHOD'],
			'VERSION'=>self::VERSION,
			'XFRAME'=>'SAMEORIGIN'
		];
		if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE) {
			unset($jar['expire']);
			session_cache_limiter('');
			call_user_func_array('session_set_cookie_params',$jar);
		}
		if (PHP_SAPI=='cli-server' &&
			preg_match('/^'.preg_quote($base,'/').'$/',$this->hive['URI']))
			$this->reroute('/');
		if (ini_get('auto_globals_jit'))
			// Override setting
			$GLOBALS+=['_ENV'=>$_ENV,'_REQUEST'=>$_REQUEST];
		// Sync PHP globals with corresponding hive keys
		$this->init=$this->hive;
		foreach (explode('|',self::GLOBALS) as $global) {
			$sync=$this->sync($global);
			$this->init+=[
				$global=>preg_match('/SERVER|ENV/',$global)?$sync:[]
			];
		}
		if ($check && $error=error_get_last())
			// Error detected
			$this->error(500,
				sprintf(self::E_Fatal,$error['message']),[$error]);
		date_default_timezone_set($this->hive['TZ']);
		// Register framework autoloader
		spl_autoload_register([$this,'autoload']);
		// Register shutdown handler
		register_shutdown_function([$this,'unload'],getcwd());
	}

}

//! Cache engine
class Cache extends Prefab {

	protected
		//! Cache DSN
		$dsn,
		//! Prefix for cache entries
		$prefix,
		//! MemCache or Redis object
		$ref;

	/**
	*	Return timestamp and TTL of cache entry or FALSE if not found
	*	@return array|FALSE
	*	@param $key string
	*	@param $val mixed
	**/
	function exists($key,&$val=NULL) {
		$fw=Base::instance();
		if (!$this->dsn)
			return FALSE;
		$ndx=$this->prefix.'.'.$key;
		$parts=explode('=',$this->dsn,2);
		switch ($parts[0]) {
			case 'apc':
			case 'apcu':
				$raw=call_user_func($parts[0].'_fetch',$ndx);
				break;
			case 'redis':
				$raw=$this->ref->get($ndx);
				break;
			case 'memcache':
				$raw=memcache_get($this->ref,$ndx);
				break;
			case 'memcached':
				$raw=$this->ref->get($ndx);
				break;
			case 'wincache':
				$raw=wincache_ucache_get($ndx);
				break;
			case 'xcache':
				$raw=xcache_get($ndx);
				break;
			case 'folder':
				$raw=$fw->read($parts[1].$ndx);
				break;
		}
		if (!empty($raw)) {
			list($val,$time,$ttl)=(array)$fw->unserialize($raw);
			if ($ttl===0 || $time+$ttl>microtime(TRUE))
				return [$time,$ttl];
			$val=null;
			$this->clear($key);
		}
		return FALSE;
	}

	/**
	*	Store value in cache
	*	@return mixed|FALSE
	*	@param $key string
	*	@param $val mixed
	*	@param $ttl int
	**/
	function set($key,$val,$ttl=0) {
		$fw=Base::instance();
		if (!$this->dsn)
			return TRUE;
		$ndx=$this->prefix.'.'.$key;
		$time=microtime(TRUE);
		if ($cached=$this->exists($key))
			list($time,$ttl)=$cached;
		$data=$fw->serialize([$val,$time,$ttl]);
		$parts=explode('=',$this->dsn,2);
		switch ($parts[0]) {
			case 'apc':
			case 'apcu':
				return call_user_func($parts[0].'_store',$ndx,$data,$ttl);
			case 'redis':
				return $this->ref->set($ndx,$data,$ttl?['ex'=>$ttl]:[]);
			case 'memcache':
				return memcache_set($this->ref,$ndx,$data,0,$ttl);
			case 'memcached':
				return $this->ref->set($ndx,$data,$ttl);
			case 'wincache':
				return wincache_ucache_set($ndx,$data,$ttl);
			case 'xcache':
				return xcache_set($ndx,$data,$ttl);
			case 'folder':
				return $fw->write($parts[1].str_replace(['/','\\'],'',$ndx),$data);
		}
		return FALSE;
	}

	/**
	*	Retrieve value of cache entry
	*	@return mixed|FALSE
	*	@param $key string
	**/
	function get($key) {
		return $this->dsn && $this->exists($key,$data)?$data:FALSE;
	}

	/**
	*	Delete cache entry
	*	@return bool
	*	@param $key string
	**/
	function clear($key) {
		if (!$this->dsn)
			return;
		$ndx=$this->prefix.'.'.$key;
		$parts=explode('=',$this->dsn,2);
		switch ($parts[0]) {
			case 'apc':
			case 'apcu':
				return call_user_func($parts[0].'_delete',$ndx);
			case 'redis':
				return $this->ref->del($ndx);
			case 'memcache':
				return memcache_delete($this->ref,$ndx);
			case 'memcached':
				return $this->ref->delete($ndx);
			case 'wincache':
				return wincache_ucache_delete($ndx);
			case 'xcache':
				return xcache_unset($ndx);
			case 'folder':
				return @unlink($parts[1].$ndx);
		}
		return FALSE;
	}

	/**
	*	Clear contents of cache backend
	*	@return bool
	*	@param $suffix string
	**/
	function reset($suffix=NULL) {
		if (!$this->dsn)
			return TRUE;
		$regex='/'.preg_quote($this->prefix.'.','/').'.*'.
			preg_quote($suffix,'/').'/';
		$parts=explode('=',$this->dsn,2);
		switch ($parts[0]) {
			case 'apc':
			case 'apcu':
				$info=call_user_func($parts[0].'_cache_info',
					$parts[0]=='apcu'?FALSE:'user');
				if (!empty($info['cache_list'])) {
					$key=array_key_exists('info',
						$info['cache_list'][0])?'info':'key';
					foreach ($info['cache_list'] as $item)
						if (preg_match($regex,$item[$key]))
							call_user_func($parts[0].'_delete',$item[$key]);
				}
				return TRUE;
			case 'redis':
				$keys=$this->ref->keys($this->prefix.'.*'.$suffix);
				foreach($keys as $key)
					$this->ref->del($key);
				return TRUE;
			case 'memcache':
				foreach (memcache_get_extended_stats(
					$this->ref,'slabs') as $slabs)
					foreach (array_filter(array_keys($slabs),'is_numeric')
						as $id)
						foreach (memcache_get_extended_stats(
							$this->ref,'cachedump',$id) as $data)
							if (is_array($data))
								foreach (array_keys($data) as $key)
									if (preg_match($regex,$key))
										memcache_delete($this->ref,$key);
				return TRUE;
			case 'memcached':
				foreach ($this->ref->getallkeys()?:[] as $key)
					if (preg_match($regex,$key))
						$this->ref->delete($key);
				return TRUE;
			case 'wincache':
				$info=wincache_ucache_info();
				foreach ($info['ucache_entries'] as $item)
					if (preg_match($regex,$item['key_name']))
						wincache_ucache_delete($item['key_name']);
				return TRUE;
			case 'xcache':
				if ($suffix && !ini_get('xcache.admin.enable_auth')) {
					$cnt=xcache_count(XC_TYPE_VAR);
					for ($i=0;$i<$cnt;$i++) {
						$list=xcache_list(XC_TYPE_VAR,$i);
						foreach ($list['cache_list'] as $item)
							if (preg_match($regex,$item['name']))
								xcache_unset($item['name']);
					}
				} else
					xcache_unset_by_prefix($this->prefix.'.');
				return TRUE;
			case 'folder':
				if ($glob=@glob($parts[1].'*'))
					foreach ($glob as $file)
						if (preg_match($regex,basename($file)))
							@unlink($file);
				return TRUE;
		}
		return FALSE;
	}

	/**
	*	Load/auto-detect cache backend
	*	@return string
	*	@param $dsn bool|string
	*	@param $seed bool|string
	**/
	function load($dsn,$seed=NULL) {
		$fw=Base::instance();
		if ($dsn=trim($dsn)) {
			if (preg_match('/^redis=(.+)/',$dsn,$parts) &&
				extension_loaded('redis')) {
				list($host,$port,$db)=explode(':',$parts[1])+[1=>6379,2=>NULL];
				$this->ref=new Redis;
				if(!$this->ref->connect($host,$port,2))
					$this->ref=NULL;
				if(isset($db))
					$this->ref->select($db);
			}
			elseif (preg_match('/^memcache=(.+)/',$dsn,$parts) &&
				extension_loaded('memcache'))
				foreach ($fw->split($parts[1]) as $server) {
					list($host,$port)=explode(':',$server)+[1=>11211];
					if (empty($this->ref))
						$this->ref=@memcache_connect($host,$port)?:NULL;
					else
						memcache_add_server($this->ref,$host,$port);
				}
			elseif (preg_match('/^memcached=(.+)/',$dsn,$parts) &&
				extension_loaded('memcached'))
				foreach ($fw->split($parts[1]) as $server) {
					list($host,$port)=explode(':',$server)+[1=>11211];
					if (empty($this->ref))
						$this->ref=new Memcached();
					$this->ref->addServer($host,$port);
				}
			if (empty($this->ref) && !preg_match('/^folder\h*=/',$dsn))
				$dsn=($grep=preg_grep('/^(apc|wincache|xcache)/',
					array_map('strtolower',get_loaded_extensions())))?
						// Auto-detect
						current($grep):
						// Use filesystem as fallback
						('folder='.$fw->TEMP.'cache/');
			if (preg_match('/^folder\h*=\h*(.+)/',$dsn,$parts) &&
				!is_dir($parts[1]))
				mkdir($parts[1],Base::MODE,TRUE);
		}
		$this->prefix=$seed?:$fw->SEED;
		return $this->dsn=$dsn;
	}

	/**
	*	Class constructor
	*	@param $dsn bool|string
	**/
	function __construct($dsn=FALSE) {
		if ($dsn)
			$this->load($dsn);
	}

}

//! View handler
class View extends Prefab {

	private
		//! Temporary hive
		$temp;

	protected
		//! Template file
		$file,
		//! Post-rendering handler
		$trigger,
		//! Nesting level
		$level=0;

	/** @var \Base Framework instance */
	protected $fw;

	function __construct() {
		$this->fw=\Base::instance();
	}

	/**
	*	Encode characters to equivalent HTML entities
	*	@return string
	*	@param $arg mixed
	**/
	function esc($arg) {
		return $this->fw->recursive($arg,
			function($val) {
				return is_string($val)?$this->fw->encode($val):$val;
			}
		);
	}

	/**
	*	Decode HTML entities to equivalent characters
	*	@return string
	*	@param $arg mixed
	**/
	function raw($arg) {
		return $this->fw->recursive($arg,
			function($val) {
				return is_string($val)?$this->fw->decode($val):$val;
			}
		);
	}

	/**
	*	Create sandbox for template execution
	*	@return string
	*	@param $hive array
	*	@param $mime string
	**/
	protected function sandbox(array $hive=NULL,$mime=NULL) {
		$fw=$this->fw;
		$implicit=FALSE;
		if (is_null($hive)) {
			$implicit=TRUE;
			$hive=$fw->hive();
		}
		if ($this->level<1 || $implicit) {
			if (!$fw->CLI && $mime && !headers_sent() &&
				!preg_grep ('/^Content-Type:/',headers_list()))
				header('Content-Type: '.$mime.'; '.
					'charset='.$fw->ENCODING);
			if ($fw->ESCAPE)
				$hive=$this->esc($hive);
			if (isset($hive['ALIASES']))
				$hive['ALIASES']=$fw->build($hive['ALIASES']);
		}
		$this->temp=$hive;
		unset($fw,$hive,$implicit,$mime);
		extract($this->temp);
		$this->temp=NULL;
		$this->level++;
		ob_start();
		require($this->file);
		$this->level--;
		return ob_get_clean();
	}

	/**
	*	Render template
	*	@return string
	*	@param $file string
	*	@param $mime string
	*	@param $hive array
	*	@param $ttl int
	**/
	function render($file,$mime='text/html',array $hive=NULL,$ttl=0) {
		$fw=$this->fw;
		$cache=Cache::instance();
		foreach ($fw->split($fw->UI) as $dir) {
			if ($cache->exists($hash=$fw->hash($dir.$file),$data))
				return $data;
			if (is_file($this->file=$fw->fixslashes($dir.$file))) {
				if (isset($_COOKIE[session_name()]) &&
					!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
					session_start();
				$fw->sync('SESSION');
				$data=$this->sandbox($hive,$mime);
				if (isset($this->trigger['afterrender']))
					foreach($this->trigger['afterrender'] as $func)
						$data=$fw->call($func,[$data, $dir.$file]);
				if ($ttl)
					$cache->set($hash,$data,$ttl);
				return $data;
			}
		}
		user_error(sprintf(Base::E_Open,$file),E_USER_ERROR);
	}

	/**
	*	post rendering handler
	*	@param $func callback
	*/
	function afterrender($func) {
		$this->trigger['afterrender'][]=$func;
	}

}

//! Lightweight template engine
class Preview extends View {

	protected
		//! token filter
		$filter=[
			'c'=>'$this->c',
			'esc'=>'$this->esc',
			'raw'=>'$this->raw',
			'alias'=>'Base::instance()->alias',
			'format'=>'Base::instance()->format'
		];

	protected
		//! newline interpolation
		$interpolation=true;

	/**
	 * enable/disable markup parsing interpolation
	 * mainly used for adding appropriate newlines
	 * @param $bool bool
	 */
	function interpolation($bool) {
		$this->interpolation=$bool;
	}

	/**
	*	Return C-locale equivalent of number
	*	@return string
	*	@param $val int|float
	**/
	function c($val) {
		$locale=setlocale(LC_NUMERIC,0);
		setlocale(LC_NUMERIC,'C');
		$out=(string)(float)$val;
		$locale=setlocale(LC_NUMERIC,$locale);
		return $out;
	}

	/**
	*	Convert token to variable
	*	@return string
	*	@param $str string
	**/
	function token($str) {
		$fw=$this->fw;
		$str=trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'),
			$fw->compile($str)));
		if (preg_match('/^(.+)(?<!\|)\|((?:\h*\w+(?:\h*[,;]?))+)$/s',
			$str,$parts)) {
			$str=trim($parts[1]);
			foreach ($fw->split(trim($parts[2],"\xC2\xA0")) as $func)
				$str=is_string($cmd=$this->filter($func))?
					$cmd.'('.$str.')':
					'Base::instance()->'.
					'call($this->filter(\''.$func.'\'),['.$str.'])';
		}
		return $str;
	}

	/**
	*	Register or get (a specific one or all) token filters
	*	@param string $key
	*	@param string|closure $func
	*	@return array|closure|string
	*/
	function filter($key=NULL,$func=NULL) {
		if (!$key)
			return array_keys($this->filter);
		$key=strtolower($key);
		if (!$func)
			return $this->filter[$key];
		$this->filter[$key]=$func;
	}

	/**
	*	Assemble markup
	*	@return string
	*	@param $node string
	**/
	protected function build($node) {
		return preg_replace_callback(
			'/\{~(.+?)~\}|\{\*(.+?)\*\}|\{\-(.+?)\-\}|'.
			'\{\{(.+?)\}\}((\r?\n)*)/s',
			function($expr) {
				if ($expr[1])
					$str='<?php '.$this->token($expr[1]).' ?>';
				elseif ($expr[2])
					return '';
				elseif ($expr[3])
					$str=$expr[3];
				else {
					$str='<?= ('.trim($this->token($expr[4])).')'.
						($this->interpolation?
							(!empty($expr[6])?'."'.$expr[6].'"':''):'').' ?>';
					if (isset($expr[5]))
						$str.=$expr[5];
				}
				return $str;
			},
			$node
		);
	}

	/**
	*	Render template string
	*	@return string
	*	@param $node string|array
	*	@param $hive array
	*	@param $ttl int
	*	@param $persist bool
	*	@param $escape bool
	**/
	function resolve($node,array $hive=NULL,$ttl=0,$persist=FALSE,$escape=NULL) {
		$fw=$this->fw;
		$cache=Cache::instance();
		if ($escape!==NULL) {
			$esc=$fw->ESCAPE;
			$fw->ESCAPE=$escape;
		}
		if ($ttl || $persist)
			$hash=$fw->hash($fw->serialize($node));
		if ($ttl && $cache->exists($hash,$data))
			return $data;
		if ($persist) {
			if (!is_dir($tmp=$fw->TEMP))
				mkdir($tmp,Base::MODE,TRUE);
			if (!is_file($this->file=($tmp.
				$fw->SEED.'.'.$hash.'.php')))
				$fw->write($this->file,$this->build($node));
			if (isset($_COOKIE[session_name()]) &&
				!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
				session_start();
			$fw->sync('SESSION');
			$data=$this->sandbox($hive);
		}
		else {
			if (!$hive)
				$hive=$fw->hive();
			if ($fw->ESCAPE)
				$hive=$this->esc($hive);
			extract($hive);
			unset($hive);
			ob_start();
			eval(' ?>'.$this->build($node).'<?php ');
			$data=ob_get_clean();
		}
		if ($ttl)
			$cache->set($hash,$data,$ttl);
		if ($escape!==NULL)
			$fw->ESCAPE=$esc;
		return $data;
	}

	/**
	 *	Parse template string
	 *	@return string
	 *	@param $text string
	 **/
	function parse($text) {
		// Remove PHP code and comments
		return preg_replace(
			'/\h*<\?(?!xml)(?:php|\s*=)?.+?\?>\h*|'.
			'\{\*.+?\*\}/is','', $text);
	}

	/**
	*	Render template
	*	@return string
	*	@param $file string
	*	@param $mime string
	*	@param $hive array
	*	@param $ttl int
	**/
	function render($file,$mime='text/html',array $hive=NULL,$ttl=0) {
		$fw=$this->fw;
		$cache=Cache::instance();
		if (!is_dir($tmp=$fw->TEMP))
			mkdir($tmp,Base::MODE,TRUE);
		foreach ($fw->split($fw->UI) as $dir) {
			if ($cache->exists($hash=$fw->hash($dir.$file),$data))
				return $data;
			if (is_file($view=$fw->fixslashes($dir.$file))) {
				if (!is_file($this->file=($tmp.
					$fw->SEED.'.'.$fw->hash($view).'.php')) ||
					filemtime($this->file)<filemtime($view)) {
					$contents=$fw->read($view);
					if (isset($this->trigger['beforerender']))
						foreach ($this->trigger['beforerender'] as $func)
							$contents=$fw->call($func, [$contents, $view]);
					$text=$this->parse($contents);
					$fw->write($this->file,$this->build($text));
				}
				if (isset($_COOKIE[session_name()]) &&
					!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
					session_start();
				$fw->sync('SESSION');
				$data=$this->sandbox($hive,$mime);
				if(isset($this->trigger['afterrender']))
					foreach ($this->trigger['afterrender'] as $func)
						$data=$fw->call($func, [$data, $view]);
				if ($ttl)
					$cache->set($hash,$data,$ttl);
				return $data;
			}
		}
		user_error(sprintf(Base::E_Open,$file),E_USER_ERROR);
	}

	/**
	 *	post rendering handler
	 *	@param $func callback
	 */
	function beforerender($func) {
		$this->trigger['beforerender'][]=$func;
	}

}

//! ISO language/country codes
class ISO extends Prefab {

	//@{ ISO 3166-1 country codes
	const
		CC_af='Afghanistan',
		CC_ax='Åland Islands',
		CC_al='Albania',
		CC_dz='Algeria',
		CC_as='American Samoa',
		CC_ad='Andorra',
		CC_ao='Angola',
		CC_ai='Anguilla',
		CC_aq='Antarctica',
		CC_ag='Antigua and Barbuda',
		CC_ar='Argentina',
		CC_am='Armenia',
		CC_aw='Aruba',
		CC_au='Australia',
		CC_at='Austria',
		CC_az='Azerbaijan',
		CC_bs='Bahamas',
		CC_bh='Bahrain',
		CC_bd='Bangladesh',
		CC_bb='Barbados',
		CC_by='Belarus',
		CC_be='Belgium',
		CC_bz='Belize',
		CC_bj='Benin',
		CC_bm='Bermuda',
		CC_bt='Bhutan',
		CC_bo='Bolivia',
		CC_bq='Bonaire, Sint Eustatius and Saba',
		CC_ba='Bosnia and Herzegovina',
		CC_bw='Botswana',
		CC_bv='Bouvet Island',
		CC_br='Brazil',
		CC_io='British Indian Ocean Territory',
		CC_bn='Brunei Darussalam',
		CC_bg='Bulgaria',
		CC_bf='Burkina Faso',
		CC_bi='Burundi',
		CC_kh='Cambodia',
		CC_cm='Cameroon',
		CC_ca='Canada',
		CC_cv='Cape Verde',
		CC_ky='Cayman Islands',
		CC_cf='Central African Republic',
		CC_td='Chad',
		CC_cl='Chile',
		CC_cn='China',
		CC_cx='Christmas Island',
		CC_cc='Cocos (Keeling) Islands',
		CC_co='Colombia',
		CC_km='Comoros',
		CC_cg='Congo',
		CC_cd='Congo, The Democratic Republic of',
		CC_ck='Cook Islands',
		CC_cr='Costa Rica',
		CC_ci='Côte d\'ivoire',
		CC_hr='Croatia',
		CC_cu='Cuba',
		CC_cw='Curaçao',
		CC_cy='Cyprus',
		CC_cz='Czech Republic',
		CC_dk='Denmark',
		CC_dj='Djibouti',
		CC_dm='Dominica',
		CC_do='Dominican Republic',
		CC_ec='Ecuador',
		CC_eg='Egypt',
		CC_sv='El Salvador',
		CC_gq='Equatorial Guinea',
		CC_er='Eritrea',
		CC_ee='Estonia',
		CC_et='Ethiopia',
		CC_fk='Falkland Islands (Malvinas)',
		CC_fo='Faroe Islands',
		CC_fj='Fiji',
		CC_fi='Finland',
		CC_fr='France',
		CC_gf='French Guiana',
		CC_pf='French Polynesia',
		CC_tf='French Southern Territories',
		CC_ga='Gabon',
		CC_gm='Gambia',
		CC_ge='Georgia',
		CC_de='Germany',
		CC_gh='Ghana',
		CC_gi='Gibraltar',
		CC_gr='Greece',
		CC_gl='Greenland',
		CC_gd='Grenada',
		CC_gp='Guadeloupe',
		CC_gu='Guam',
		CC_gt='Guatemala',
		CC_gg='Guernsey',
		CC_gn='Guinea',
		CC_gw='Guinea-Bissau',
		CC_gy='Guyana',
		CC_ht='Haiti',
		CC_hm='Heard Island and McDonald Islands',
		CC_va='Holy See (Vatican City State)',
		CC_hn='Honduras',
		CC_hk='Hong Kong',
		CC_hu='Hungary',
		CC_is='Iceland',
		CC_in='India',
		CC_id='Indonesia',
		CC_ir='Iran, Islamic Republic of',
		CC_iq='Iraq',
		CC_ie='Ireland',
		CC_im='Isle of Man',
		CC_il='Israel',
		CC_it='Italy',
		CC_jm='Jamaica',
		CC_jp='Japan',
		CC_je='Jersey',
		CC_jo='Jordan',
		CC_kz='Kazakhstan',
		CC_ke='Kenya',
		CC_ki='Kiribati',
		CC_kp='Korea, Democratic People\'s Republic of',
		CC_kr='Korea, Republic of',
		CC_kw='Kuwait',
		CC_kg='Kyrgyzstan',
		CC_la='Lao People\'s Democratic Republic',
		CC_lv='Latvia',
		CC_lb='Lebanon',
		CC_ls='Lesotho',
		CC_lr='Liberia',
		CC_ly='Libya',
		CC_li='Liechtenstein',
		CC_lt='Lithuania',
		CC_lu='Luxembourg',
		CC_mo='Macao',
		CC_mk='Macedonia, The Former Yugoslav Republic of',
		CC_mg='Madagascar',
		CC_mw='Malawi',
		CC_my='Malaysia',
		CC_mv='Maldives',
		CC_ml='Mali',
		CC_mt='Malta',
		CC_mh='Marshall Islands',
		CC_mq='Martinique',
		CC_mr='Mauritania',
		CC_mu='Mauritius',
		CC_yt='Mayotte',
		CC_mx='Mexico',
		CC_fm='Micronesia, Federated States of',
		CC_md='Moldova, Republic of',
		CC_mc='Monaco',
		CC_mn='Mongolia',
		CC_me='Montenegro',
		CC_ms='Montserrat',
		CC_ma='Morocco',
		CC_mz='Mozambique',
		CC_mm='Myanmar',
		CC_na='Namibia',
		CC_nr='Nauru',
		CC_np='Nepal',
		CC_nl='Netherlands',
		CC_nc='New Caledonia',
		CC_nz='New Zealand',
		CC_ni='Nicaragua',
		CC_ne='Niger',
		CC_ng='Nigeria',
		CC_nu='Niue',
		CC_nf='Norfolk Island',
		CC_mp='Northern Mariana Islands',
		CC_no='Norway',
		CC_om='Oman',
		CC_pk='Pakistan',
		CC_pw='Palau',
		CC_ps='Palestinian Territory, Occupied',
		CC_pa='Panama',
		CC_pg='Papua New Guinea',
		CC_py='Paraguay',
		CC_pe='Peru',
		CC_ph='Philippines',
		CC_pn='Pitcairn',
		CC_pl='Poland',
		CC_pt='Portugal',
		CC_pr='Puerto Rico',
		CC_qa='Qatar',
		CC_re='Réunion',
		CC_ro='Romania',
		CC_ru='Russian Federation',
		CC_rw='Rwanda',
		CC_bl='Saint Barthélemy',
		CC_sh='Saint Helena, Ascension and Tristan da Cunha',
		CC_kn='Saint Kitts and Nevis',
		CC_lc='Saint Lucia',
		CC_mf='Saint Martin (French Part)',
		CC_pm='Saint Pierre and Miquelon',
		CC_vc='Saint Vincent and The Grenadines',
		CC_ws='Samoa',
		CC_sm='San Marino',
		CC_st='Sao Tome and Principe',
		CC_sa='Saudi Arabia',
		CC_sn='Senegal',
		CC_rs='Serbia',
		CC_sc='Seychelles',
		CC_sl='Sierra Leone',
		CC_sg='Singapore',
		CC_sk='Slovakia',
		CC_sx='Sint Maarten (Dutch Part)',
		CC_si='Slovenia',
		CC_sb='Solomon Islands',
		CC_so='Somalia',
		CC_za='South Africa',
		CC_gs='South Georgia and The South Sandwich Islands',
		CC_ss='South Sudan',
		CC_es='Spain',
		CC_lk='Sri Lanka',
		CC_sd='Sudan',
		CC_sr='Suriname',
		CC_sj='Svalbard and Jan Mayen',
		CC_sz='Swaziland',
		CC_se='Sweden',
		CC_ch='Switzerland',
		CC_sy='Syrian Arab Republic',
		CC_tw='Taiwan, Province of China',
		CC_tj='Tajikistan',
		CC_tz='Tanzania, United Republic of',
		CC_th='Thailand',
		CC_tl='Timor-Leste',
		CC_tg='Togo',
		CC_tk='Tokelau',
		CC_to='Tonga',
		CC_tt='Trinidad and Tobago',
		CC_tn='Tunisia',
		CC_tr='Turkey',
		CC_tm='Turkmenistan',
		CC_tc='Turks and Caicos Islands',
		CC_tv='Tuvalu',
		CC_ug='Uganda',
		CC_ua='Ukraine',
		CC_ae='United Arab Emirates',
		CC_gb='United Kingdom',
		CC_us='United States',
		CC_um='United States Minor Outlying Islands',
		CC_uy='Uruguay',
		CC_uz='Uzbekistan',
		CC_vu='Vanuatu',
		CC_ve='Venezuela',
		CC_vn='Viet Nam',
		CC_vg='Virgin Islands, British',
		CC_vi='Virgin Islands, U.S.',
		CC_wf='Wallis and Futuna',
		CC_eh='Western Sahara',
		CC_ye='Yemen',
		CC_zm='Zambia',
		CC_zw='Zimbabwe';
	//@}

	//@{ ISO 639-1 language codes (Windows-compatibility subset)
	const
		LC_af='Afrikaans',
		LC_am='Amharic',
		LC_ar='Arabic',
		LC_as='Assamese',
		LC_ba='Bashkir',
		LC_be='Belarusian',
		LC_bg='Bulgarian',
		LC_bn='Bengali',
		LC_bo='Tibetan',
		LC_br='Breton',
		LC_ca='Catalan',
		LC_co='Corsican',
		LC_cs='Czech',
		LC_cy='Welsh',
		LC_da='Danish',
		LC_de='German',
		LC_dv='Divehi',
		LC_el='Greek',
		LC_en='English',
		LC_es='Spanish',
		LC_et='Estonian',
		LC_eu='Basque',
		LC_fa='Persian',
		LC_fi='Finnish',
		LC_fo='Faroese',
		LC_fr='French',
		LC_gd='Scottish Gaelic',
		LC_gl='Galician',
		LC_gu='Gujarati',
		LC_he='Hebrew',
		LC_hi='Hindi',
		LC_hr='Croatian',
		LC_hu='Hungarian',
		LC_hy='Armenian',
		LC_id='Indonesian',
		LC_ig='Igbo',
		LC_is='Icelandic',
		LC_it='Italian',
		LC_ja='Japanese',
		LC_ka='Georgian',
		LC_kk='Kazakh',
		LC_km='Khmer',
		LC_kn='Kannada',
		LC_ko='Korean',
		LC_lb='Luxembourgish',
		LC_lo='Lao',
		LC_lt='Lithuanian',
		LC_lv='Latvian',
		LC_mi='Maori',
		LC_ml='Malayalam',
		LC_mr='Marathi',
		LC_ms='Malay',
		LC_mt='Maltese',
		LC_ne='Nepali',
		LC_nl='Dutch',
		LC_no='Norwegian',
		LC_oc='Occitan',
		LC_or='Oriya',
		LC_pl='Polish',
		LC_ps='Pashto',
		LC_pt='Portuguese',
		LC_qu='Quechua',
		LC_ro='Romanian',
		LC_ru='Russian',
		LC_rw='Kinyarwanda',
		LC_sa='Sanskrit',
		LC_si='Sinhala',
		LC_sk='Slovak',
		LC_sl='Slovenian',
		LC_sq='Albanian',
		LC_sv='Swedish',
		LC_ta='Tamil',
		LC_te='Telugu',
		LC_th='Thai',
		LC_tk='Turkmen',
		LC_tr='Turkish',
		LC_tt='Tatar',
		LC_uk='Ukrainian',
		LC_ur='Urdu',
		LC_vi='Vietnamese',
		LC_wo='Wolof',
		LC_yo='Yoruba',
		LC_zh='Chinese';
	//@}

	/**
	*	Return list of languages indexed by ISO 639-1 language code
	*	@return array
	**/
	function languages() {
		return \Base::instance()->constants($this,'LC_');
	}

	/**
	*	Return list of countries indexed by ISO 3166-1 country code
	*	@return array
	**/
	function countries() {
		return \Base::instance()->constants($this,'CC_');
	}

}

//! Container for singular object instances
final class Registry {

	private static
		//! Object catalog
		$table;

	/**
	*	Return TRUE if object exists in catalog
	*	@return bool
	*	@param $key string
	**/
	static function exists($key) {
		return isset(self::$table[$key]);
	}

	/**
	*	Add object to catalog
	*	@return object
	*	@param $key string
	*	@param $obj object
	**/
	static function set($key,$obj) {
		return self::$table[$key]=$obj;
	}

	/**
	*	Retrieve object from catalog
	*	@return object
	*	@param $key string
	**/
	static function get($key) {
		return self::$table[$key];
	}

	/**
	*	Delete object from catalog
	*	@return NULL
	*	@param $key string
	**/
	static function clear($key) {
		self::$table[$key]=NULL;
		unset(self::$table[$key]);
	}

	//! Prohibit cloning
	private function __clone() {
	}

	//! Prohibit instantiation
	private function __construct() {
	}

}

return Base::instance();
diff --git a/vendor/fatfree-3.6/lib/basket.php b/vendor/fatfree-3.6/lib/basket.php
new file mode 100755
index 0000000..f939c7c
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/basket.php
@@ -0,0 +1,238 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Session-based pseudo-mapper
class Basket extends Magic {

	//@{ Error messages
	const
		E_Field='Undefined field %s';
	//@}

	protected
		//! Session key
		$key,
		//! Current item identifier
		$id,
		//! Current item contents
		$item=[];

	/**
	*	Return TRUE if field is defined
	*	@return bool
	*	@param $key string
	**/
	function exists($key) {
		return array_key_exists($key,$this->item);
	}

	/**
	*	Assign value to field
	*	@return scalar|FALSE
	*	@param $key string
	*	@param $val scalar
	**/
	function set($key,$val) {
		return ($key=='_id')?FALSE:($this->item[$key]=$val);
	}

	/**
	*	Retrieve value of field
	*	@return scalar|FALSE
	*	@param $key string
	**/
	function &get($key) {
		if ($key=='_id')
			return $this->id;
		if (array_key_exists($key,$this->item))
			return $this->item[$key];
		user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
		return FALSE;
	}

	/**
	*	Delete field
	*	@return NULL
	*	@param $key string
	**/
	function clear($key) {
		unset($this->item[$key]);
	}

	/**
	*	Return items that match key/value pair;
	*	If no key/value pair specified, return all items
	*	@return array
	*	@param $key string
	*	@param $val mixed
	**/
	function find($key=NULL,$val=NULL) {
		$out=[];
		if (isset($_SESSION[$this->key])) {
			foreach ($_SESSION[$this->key] as $id=>$item)
				if (!isset($key) ||
					array_key_exists($key,$item) && $item[$key]==$val) {
					$obj=clone($this);
					$obj->id=$id;
					$obj->item=$item;
					$out[]=$obj;
				}
		}
		return $out;
	}

	/**
	*	Return first item that matches key/value pair
	*	@return object|FALSE
	*	@param $key string
	*	@param $val mixed
	**/
	function findone($key,$val) {
		return ($data=$this->find($key,$val))?$data[0]:FALSE;
	}

	/**
	*	Map current item to matching key/value pair
	*	@return array
	*	@param $key string
	*	@param $val mixed
	**/
	function load($key,$val) {
		if ($found=$this->find($key,$val)) {
			$this->id=$found[0]->id;
			return $this->item=$found[0]->item;
		}
		$this->reset();
		return [];
	}

	/**
	*	Return TRUE if current item is empty/undefined
	*	@return bool
	**/
	function dry() {
		return !$this->item;
	}

	/**
	*	Return number of items in basket
	*	@return int
	**/
	function count() {
		return isset($_SESSION[$this->key])?count($_SESSION[$this->key]):0;
	}

	/**
	*	Save current item
	*	@return array
	**/
	function save() {
		if (!$this->id)
			$this->id=uniqid(NULL,TRUE);
		$_SESSION[$this->key][$this->id]=$this->item;
		return $this->item;
	}

	/**
	*	Erase item matching key/value pair
	*	@return bool
	*	@param $key string
	*	@param $val mixed
	**/
	function erase($key,$val) {
		$found=$this->find($key,$val);
		if ($found && $id=$found[0]->id) {
			unset($_SESSION[$this->key][$id]);
			if ($id==$this->id)
				$this->reset();
			return TRUE;
		}
		return FALSE;
	}

	/**
	*	Reset cursor
	*	@return NULL
	**/
	function reset() {
		$this->id=NULL;
		$this->item=[];
	}

	/**
	*	Empty basket
	*	@return NULL
	**/
	function drop() {
		unset($_SESSION[$this->key]);
	}

	/**
	*	Hydrate item using hive array variable
	*	@return NULL
	*	@param $var array|string
	**/
	function copyfrom($var) {
		if (is_string($var))
			$var=\Base::instance()->$var;
		foreach ($var as $key=>$val)
			$this->set($key,$val);
	}

	/**
	*	Populate hive array variable with item contents
	*	@return NULL
	*	@param $key string
	**/
	function copyto($key) {
		$var=&\Base::instance()->ref($key);
		foreach ($this->item as $key=>$field)
			$var[$key]=$field;
	}

	/**
	*	Check out basket contents
	*	@return array
	**/
	function checkout() {
		if (isset($_SESSION[$this->key])) {
			$out=$_SESSION[$this->key];
			unset($_SESSION[$this->key]);
			return $out;
		}
		return [];
	}

	/**
	*	Instantiate class
	*	@return void
	*	@param $key string
	**/
	function __construct($key='basket') {
		$this->key=$key;
		if (session_status()!=PHP_SESSION_ACTIVE)
			session_start();
		Base::instance()->sync('SESSION');
		$this->reset();
	}

}
diff --git a/vendor/fatfree-3.6/lib/bcrypt.php b/vendor/fatfree-3.6/lib/bcrypt.php
new file mode 100755
index 0000000..2355471
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/bcrypt.php
@@ -0,0 +1,96 @@
<?php

/**
*	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.
*
*	This file is part of the Fat-Free Framework (http://fatfreeframework.com).
*
*	This is free software: you can redistribute it and/or modify it under the
*	terms of the GNU General Public License as published by the Free Software
*	Foundation, either version 3 of the License, or later.
*
*	Fat-Free Framework is distributed in the hope that it will be useful,
*	but WITHOUT ANY WARRANTY; without even the implied warranty of
*	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*	General Public License for more details.
*
*	You should have received a copy of the GNU General Public License along
*	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.
*
**/

/**
*	Lightweight password hashing library (PHP 5.5+ only)
*	@deprecated Use http://php.net/manual/en/ref.password.php instead
**/
class Bcrypt extends Prefab {

	//@{ Error messages
	const
		E_CostArg='Invalid cost parameter',
		E_SaltArg='Salt must be at least 22 alphanumeric characters';
	//@}

	//! Default cost
	const
		COST=10;

	/**
	*	Generate bcrypt hash of string
	*	@return string|FALSE
	*	@param $pw string
	*	@param $salt string
	*	@param $cost int
	**/
	function hash($pw,$salt=NULL,$cost=self::COST) {
		if ($cost<4 || $cost>31)
			user_error(self::E_CostArg,E_USER_ERROR);
		$len=22;
		if ($salt) {
			if (!preg_match('/^[[:alnum:]\.\/]{'.$len.',}$/',$salt))
				user_error(self::E_SaltArg,E_USER_ERROR);
		}
		else {
			$raw=16;
			$iv='';
			if (!$iv && extension_loaded('openssl'))
				$iv=openssl_random_pseudo_bytes($raw);
			if (!$iv)
				for ($i=0;$i<$raw;$i++)
					$iv.=chr(mt_rand(0,255));
			$salt=str_replace('+','.',base64_encode($iv));
		}
		$salt=substr($salt,0,$len);
		$hash=crypt($pw,sprintf('$2y$%02d$',$cost).$salt);
		return strlen($hash)>13?$hash:FALSE;
	}

	/**
	*	Check if password is still strong enough
	*	@return bool
	*	@param $hash string
	*	@param $cost int
	**/
	function needs_rehash($hash,$cost=self::COST) {
		list($pwcost)=sscanf($hash,"$2y$%d$");
		return $pwcost<$cost;
	}

	/**
	*	Verify password against hash using timing attack resistant approach
	*	@return bool
	*	@param $pw string
	*	@param $hash string
	**/
	function verify($pw,$hash) {
		$val=crypt($pw,$hash);
		$len=strlen($val);
		if ($len!=strlen($hash) || $len<14)
			return FALSE;
		$out=0;
		for ($i=0;$i<$len;$i++)
			$out|=(ord($val[$i])^ord($hash[$i]));
		return $out===0;
	}

}
diff --git a/vendor/fatfree-3.6/lib/cli/ws.php b/vendor/fatfree-3.6/lib/cli/ws.php
new file mode 100755
index 0000000..c11e8ad
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/cli/ws.php
@@ -0,0 +1,516 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace CLI;

//! RFC6455 WebSocket server
class WS {

	const
		//! UUID magic string
		Magic='258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
		//! Max packet size
		Packet=65536;

	//@{ Mask bits for first byte of header
	const
		Text=0x01,
		Binary=0x02,
		Close=0x08,
		Ping=0x09,
		Pong=0x0a,
		OpCode=0x0f,
		Finale=0x80;
	//@}

	//@{ Mask bits for second byte of header
	const
		Length=0x7f;
	//@}

	protected
		$addr,
		$ctx,
		$wait,
		$sockets,
		$agents=[],
		$events=[];

	/**
	*	Allocate stream socket
	*	@return NULL
	*	@param $socket resource
	**/
	function alloc($socket) {
		if (is_bool($str=$this->read($socket))) {
			$this->close($socket);
			return;
		}
		// Get WebSocket headers
		$hdrs=[];
		$CRLF="\r\n";
		$verb=NULL;
		$uri=NULL;
		foreach (explode($CRLF,trim($str)) as $line)
			if (preg_match('/^(\w+)\s(.+)\sHTTP\/1\.\d$/',
				trim($line),$match)) {
				$verb=$match[1];
				$uri=$match[2];
			}
			else
			if (preg_match('/^(.+): (.+)/',trim($line),$match))
				// Standardize header
				$hdrs[
					strtr(
						ucwords(
							strtolower(
								strtr($match[1],'-',' ')
							)
						),' ','-'
					)
				]=$match[2];
			else {
				$this->close($socket);
				return;
			}
		if (empty($hdrs['Upgrade']) &&
			empty($hdrs['Sec-Websocket-Key'])) {
			// Not a WebSocket request
			if ($verb && $uri)
				$this->write(
					$socket,
					$str='HTTP/1.1 400 Bad Request'.$CRLF.
						'Connection: close'.$CRLF.$CRLF
				);
			$this->close($socket);
			return;
		}
		// Handshake
		$bytes=$this->write(
			$socket,
			$str='HTTP/1.1 101 Switching Protocols'.$CRLF.
				'Upgrade: websocket'.$CRLF.
				'Connection: Upgrade'.$CRLF.
				'Sec-WebSocket-Accept: '.
					base64_encode(
						sha1(
							$hdrs['Sec-Websocket-Key'].
							self::Magic,
							TRUE
						)
					).$CRLF.$CRLF
		);
		if ($bytes) {
			// Connect agent to server
			$this->sockets[]=$socket;
			$this->agents[(int)$socket]=
				new Agent($this,$socket,$verb,$uri,$hdrs);
		}
		else
			$this->close($socket);
	}

	/**
	*	Close stream socket
	*	@return NULL
	*	@param $socket resource
	**/
	function close($socket) {
		stream_socket_shutdown($socket,STREAM_SHUT_WR);
		@fclose($socket);
	}

	/**
	*	Free stream socket
	*	@return bool
	*	@param $socket resource
	**/
	function free($socket) {
		unset($this->sockets[array_search($socket,$this->sockets)]);
		unset($this->agents[(int)$socket]);
		$this->close($socket);
	}

	/**
	*	Read from stream socket
	*	@return string|FALSE
	*	@param $socket resource
	**/
	function read($socket) {
		if (is_string($str=@fread($socket,self::Packet)) &&
			strlen($str) &&
			strlen($str)<self::Packet)
			return $str;
		if (isset($this->events['error']) &&
			is_callable($func=$this->events['error']))
			$func($this);
		return FALSE;
	}

	/**
	*	Write to stream socket
	*	@return int|FALSE
	*	@param $socket resource
	*	@param $str string
	**/
	function write($socket,$str) {
		for ($i=0,$bytes=0;$i<strlen($str);$i+=$bytes) {
			if (($bytes=@fwrite($socket,substr($str,$i))) &&
				@fflush($socket))
				continue;
			if (isset($this->events['error']) &&
				is_callable($func=$this->events['error']))
				$func($this);
			return FALSE;
		}
		return $bytes;
	}

	/**
	*	Return socket agents
	*	@return array
	*	@param $uri string
	***/
	function agents($uri=NULL) {
		return array_filter(
			$this->agents,
			function($val) use($uri) {
				return $uri?($val->uri()==$uri):TRUE;
			}
		);
	}

	/**
	*	Return event handlers
	*	@return array
	**/
	function events() {
		return $this->events;
	}

	/**
	*	Bind function to event handler
	*	@return object
	*	@param $event string
	*	@param $func callable
	**/
	function on($event,$func) {
		$this->events[$event]=$func;
		return $this;
	}

	/**
	*	Terminate server
	*	@return NULL
	*	@param $signal int
	**/
	function kill($signal) {
		die;
	}

	/**
	*	Execute the server process
	*	@return object
	**/
	function run() {
		$fw=\Base::instance();
		// Assign signal handlers
		declare(ticks=1);
		pcntl_signal(SIGINT,[$this,'kill']);
		pcntl_signal(SIGTERM,[$this,'kill']);
		gc_enable();
		// Activate WebSocket listener
		$listen=stream_socket_server(
			$this->addr,$errno,$errstr,
			STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
			$this->ctx
		);
		$socket=socket_import_stream($listen);
		register_shutdown_function(function() use($listen) {
			foreach ($this->sockets as $socket)
				if ($socket!=$listen)
					$this->free($socket);
			$this->close($listen);
			if (isset($this->events['stop']) &&
				is_callable($func=$this->events['stop']))
				$func($this);
		});
		if ($errstr)
			user_error($errstr,E_USER_ERROR);
		if (isset($this->events['start']) &&
			is_callable($func=$this->events['start']))
			$func($this);
		$this->sockets=[$listen];
		$empty=[];
		$wait=$this->wait;
		while (TRUE) {
			$active=$this->sockets;
			$mark=microtime(TRUE);
			$count=@stream_select(
				$active,$empty,$empty,(int)$wait,round(1e6*($wait-(int)$wait))
			);
			if (is_bool($count) && $wait) {
				if (isset($this->events['error']) &&
					is_callable($func=$this->events['error']))
					$func($this);
				die;
			}
			if ($count) {
				// Process active connections
				foreach ($active as $socket) {
					if (!is_resource($socket))
						continue;
					if ($socket==$listen) {
						if ($socket=@stream_socket_accept($listen,0))
							$this->alloc($socket);
						else
						if (isset($this->events['error']) &&
							is_callable($func=$this->events['error']))
							$func($this);
					}
					else {
						$id=(int)$socket;
						if (isset($this->agents[$id]) &&
							$raw=$this->agents[$id]->fetch()) {
							list($op,$data)=$raw;
							// Dispatch
							switch ($op & self::OpCode) {
							case self::Ping:
								$this->agents[$id]->send(self::Pong);
								break;
							case self::Close:
								$this->free($socket);
								break;
							case self::Text:
								$data=trim($data);
							case self::Binary:
								if (isset($this->events['receive']) &&
									is_callable($func=$this->events['receive']))
									$func($this->agents[$id],$op,$data);
								break;
							}
						}
					}
				}
				$wait-=microtime(TRUE)-$mark;
				while ($wait<1e-6) {
					$wait+=$this->wait;
					$count=0;
				}
			}
			if (!$count) {
				$mark=microtime(TRUE);
				foreach ($this->sockets as $socket) {
					if (!is_resource($socket))
						continue;
					$id=(int)$socket;
					if ($socket!=$listen &&
						isset($this->agents[$id]) &&
						isset($this->events['idle']) &&
						is_callable($func=$this->events['idle']))
						$func($this->agents[$id]);
				}
				$wait=$this->wait-microtime(TRUE)+$mark;
			}
			gc_collect_cycles();
		}
	}

	/**
	*	Instantiate object
	*	@return object
	*	@param $addr string
	*	@param $ctx resource
	*	@param $wait int
	**/
	function __construct($addr,$ctx=NULL,$wait=60) {
		$this->addr=$addr;
		$this->ctx=$ctx?:stream_context_create();
		$this->wait=$wait;
		$this->events=[];
	}

}

//! RFC6455 remote socket
class Agent {

	protected
		$server,
		$id,
		$socket,
		$flag,
		$verb,
		$uri,
		$headers,
		$events,
		$buffer;

	/**
	*	Return server instance
	*	@return object
	**/
	function server() {
		return $this->server;
	}

	/**
	*	Return socket ID
	*	@return string
	**/
	function id() {
		return $this->id;
	}

	/**
	*	Return request method
	*	@return string
	**/
	function verb() {
		return $this->verb;
	}

	/**
	*	Return request URI
	*	@return string
	**/
	function uri() {
		return $this->uri;
	}

	/**
	*	Return socket headers
	*	@return string
	**/
	function headers() {
		return $this->headers;
	}

	/**
	*	Frame and transmit payload
	*	@return string|FALSE
	*	@param $socket resource
	*	@param $op int
	*	@param $payload string
	**/
	function send($op,$data='') {
		$mask=WS::Finale | $op & WS::OpCode;
		$len=strlen($data);
		$str='';
		if ($len>0xffff)
			$str=pack('CCNN',$mask,0x7f,$len);
		else
		if ($len>0x7d)
			$str=pack('CCn',$mask,0x7e,$len);
		else
			$str=pack('CC',$mask,$len);
		$str.=$data;
		$server=$this->server();
		if (is_bool($server->write($this->socket,$str))) {
			$this->free();
			return FALSE;
		}
		if (!in_array($op,[WS::Pong,WS::Close]) &&
			isset($this->events['send']) &&
			is_callable($func=$this->events['send']))
			$func($this,$op,$data);
		return $data;
	}

	/**
	*	Retrieve and unmask payload
	*	@return array|FALSE
	**/
	function fetch() {
		// Unmask payload
		$server=$this->server();
		if (is_bool($buf=$server->read($this->socket))) {
			$this->free();
			return FALSE;
		}
		$buf=($this->buffer.=$buf);
		$op=ord($buf[0]) & WS::OpCode;
		$len=ord($buf[1]) & WS::Length;
		$pos=2;
		if ($len==0x7e) {
			$len=ord($buf[2])*256+ord($buf[3]);
			$pos+=2;
		}
		else
		if ($len==0x7f) {
			for ($i=0,$len=0;$i<8;$i++)
				$len=$len*256+ord($buf[$i+2]);
			$pos+=8;
		}
		for ($i=0,$mask=[];$i<4;$i++)
			$mask[$i]=ord($buf[$pos+$i]);
		$pos+=4;
		if (strlen($buf)<$len+$pos)
			return FALSE;
		for ($i=0,$data='';$i<$len;$i++)
			$data.=chr(ord($buf[$pos+$i])^$mask[$i%4]);
		$this->buffer='';
		return [$op,$data];
	}

	/**
	*	Free stream socket
	*	@return NULL
	**/
	function free() {
		$this->server->free($this->socket);
	}

	/**
	*	Destroy object
	*	@return NULL
	**/
	function __destruct() {
		if (isset($this->events['disconnect']) &&
			is_callable($func=$this->events['disconnect']))
			$func($this);
	}

	/**
	*	Instantiate object
	*	@return object
	*	@param $server object
	*	@param $socket resource
	*	@param $verb string
	*	@param $uri string
	*	@param $hdrs array
	**/
	function __construct($server,$socket,$verb,$uri,array $hdrs) {
		$this->server=$server;
		$this->id=stream_socket_get_name($socket,TRUE);
		$this->socket=$socket;
		$this->verb=$verb;
		$this->uri=$uri;
		$this->headers=$hdrs;
		$this->events=$server->events();
		$this->buffer='';
		if (isset($this->events['connect']) &&
			is_callable($func=$this->events['connect']))
			$func($this);
	}

}
diff --git a/vendor/fatfree-3.6/lib/code.css b/vendor/fatfree-3.6/lib/code.css
new file mode 100755
index 0000000..618703f
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/code.css
@@ -0,0 +1 @@
code{word-wrap:break-word;color:black}.comment,.doc_comment,.ml_comment{color:dimgray;font-style:italic}.variable{color:blueviolet}.const,.constant_encapsed_string,.class_c,.dir,.file,.func_c,.halt_compiler,.line,.method_c,.lnumber,.dnumber{color:crimson}.string,.and_equal,.boolean_and,.boolean_or,.concat_equal,.dec,.div_equal,.inc,.is_equal,.is_greater_or_equal,.is_identical,.is_not_equal,.is_not_identical,.is_smaller_or_equal,.logical_and,.logical_or,.logical_xor,.minus_equal,.mod_equal,.mul_equal,.ns_c,.ns_separator,.or_equal,.plus_equal,.sl,.sl_equal,.sr,.sr_equal,.xor_equal,.start_heredoc,.end_heredoc,.object_operator,.paamayim_nekudotayim{color:black}.abstract,.array,.array_cast,.as,.break,.case,.catch,.class,.clone,.continue,.declare,.default,.do,.echo,.else,.elseif,.empty.enddeclare,.endfor,.endforach,.endif,.endswitch,.endwhile,.eval,.exit,.extends,.final,.for,.foreach,.function,.global,.goto,.if,.implements,.include,.include_once,.instanceof,.interface,.isset,.list,.namespace,.new,.print,.private,.public,.protected,.require,.require_once,.return,.static,.switch,.throw,.try,.unset,.use,.var,.while{color:royalblue}.open_tag,.open_tag_with_echo,.close_tag{color:orange}.ini_section{color:black}.ini_key{color:royalblue}.ini_value{color:crimson}.xml_tag{color:dodgerblue}.xml_attr{color:blueviolet}.xml_data{color:red}.section{color:black}.directive{color:blue}.data{color:dimgray}
diff --git a/vendor/fatfree-3.6/lib/db/cursor.php b/vendor/fatfree-3.6/lib/db/cursor.php
new file mode 100755
index 0000000..6edbc0e
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/cursor.php
@@ -0,0 +1,388 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB;

//! Simple cursor implementation
abstract class Cursor extends \Magic implements \IteratorAggregate {

	//@{ Error messages
	const
		E_Field='Undefined field %s';
	//@}

	protected
		//! Query results
		$query=[],
		//! Current position
		$ptr=0,
		//! Event listeners
		$trigger=[];

	/**
	*	Return database type
	*	@return string
	**/
	abstract function dbtype();

	/**
	*	Return field names
	*	@return array
	**/
	abstract function fields();

	/**
	*	Return fields of mapper object as an associative array
	*	@return array
	*	@param $obj object
	**/
	abstract function cast($obj=NULL);

	/**
	*	Return records (array of mapper objects) that match criteria
	*	@return array
	*	@param $filter string|array
	*	@param $options array
	*	@param $ttl int
	**/
	abstract function find($filter=NULL,array $options=NULL,$ttl=0);

	/**
	*	Count records that match criteria
	*	@return int
	*	@param $filter array
	*	@param $options array
	*	@param $ttl int
	**/
	abstract function count($filter=NULL,array $options=NULL,$ttl=0);

	/**
	*	Insert new record
	*	@return array
	**/
	abstract function insert();

	/**
	*	Update current record
	*	@return array
	**/
	abstract function update();

	/**
	*	Hydrate mapper object using hive array variable
	*	@return NULL
	*	@param $var array|string
	*	@param $func callback
	**/
	abstract function copyfrom($var,$func=NULL);

	/**
	*	Populate hive array variable with mapper fields
	*	@return NULL
	*	@param $key string
	**/
	abstract function copyto($key);

	/**
	*	Get cursor's equivalent external iterator
	*	Causes a fatal error in PHP 5.3.5 if uncommented
	*	return ArrayIterator
	**/
	abstract function getiterator();


	/**
	*	Return TRUE if current cursor position is not mapped to any record
	*	@return bool
	**/
	function dry() {
		return empty($this->query[$this->ptr]);
	}

	/**
	*	Return first record (mapper object) that matches criteria
	*	@return static|FALSE
	*	@param $filter string|array
	*	@param $options array
	*	@param $ttl int
	**/
	function findone($filter=NULL,array $options=NULL,$ttl=0) {
		if (!$options)
			$options=[];
		// Override limit
		$options['limit']=1;
		return ($data=$this->find($filter,$options,$ttl))?$data[0]:FALSE;
	}

	/**
	*	Return array containing subset of records matching criteria,
	*	total number of records in superset, specified limit, number of
	*	subsets available, and actual subset position
	*	@return array
	*	@param $pos int
	*	@param $size int
	*	@param $filter string|array
	*	@param $options array
	*	@param $ttl int
	*	@param $bounce bool
	**/
	function paginate(
		$pos=0,$size=10,$filter=NULL,array $options=NULL,$ttl=0,$bounce=TRUE) {
		$total=$this->count($filter,$options,$ttl);
		$count=ceil($total/$size);
		if ($bounce)
			$pos=max(0,min($pos,$count-1));
		return [
			'subset'=>($bounce || $pos<$count)?$this->find($filter,
				array_merge(
					$options?:[],
					['limit'=>$size,'offset'=>$pos*$size]
				),
				$ttl
			):[],
			'total'=>$total,
			'limit'=>$size,
			'count'=>$count,
			'pos'=>$bounce?($pos<$count?$pos:0):$pos
		];
	}

	/**
	*	Map to first record that matches criteria
	*	@return array|FALSE
	*	@param $filter string|array
	*	@param $options array
	*	@param $ttl int
	**/
	function load($filter=NULL,array $options=NULL,$ttl=0) {
		$this->reset();
		return ($this->query=$this->find($filter,$options,$ttl)) &&
			$this->skip(0)?$this->query[$this->ptr]:FALSE;
	}

	/**
	*	Return the count of records loaded
	*	@return int
	**/
	function loaded() {
		return count($this->query);
	}

	/**
	*	Map to first record in cursor
	*	@return mixed
	**/
	function first() {
		return $this->skip(-$this->ptr);
	}

	/**
	*	Map to last record in cursor
	*	@return mixed
	**/
	function last() {
		return $this->skip(($ofs=count($this->query)-$this->ptr)?$ofs-1:0);
	}

	/**
	*	Map to nth record relative to current cursor position
	*	@return mixed
	*	@param $ofs int
	**/
	function skip($ofs=1) {
		$this->ptr+=$ofs;
		return $this->ptr>-1 && $this->ptr<count($this->query)?
			$this->query[$this->ptr]:FALSE;
	}

	/**
	*	Map next record
	*	@return mixed
	**/
	function next() {
		return $this->skip();
	}

	/**
	*	Map previous record
	*	@return mixed
	**/
	function prev() {
		return $this->skip(-1);
	}

	/**
	 * Return whether current iterator position is valid.
	 */
	function valid() {
		return !$this->dry();
	}

	/**
	*	Save mapped record
	*	@return mixed
	**/
	function save() {
		return $this->query?$this->update():$this->insert();
	}

	/**
	*	Delete current record
	*	@return int|bool
	**/
	function erase() {
		$this->query=array_slice($this->query,0,$this->ptr,TRUE)+
			array_slice($this->query,$this->ptr,NULL,TRUE);
		$this->skip(0);
	}

	/**
	*	Define onload trigger
	*	@return callback
	*	@param $func callback
	**/
	function onload($func) {
		return $this->trigger['load']=$func;
	}

	/**
	*	Define beforeinsert trigger
	*	@return callback
	*	@param $func callback
	**/
	function beforeinsert($func) {
		return $this->trigger['beforeinsert']=$func;
	}

	/**
	*	Define afterinsert trigger
	*	@return callback
	*	@param $func callback
	**/
	function afterinsert($func) {
		return $this->trigger['afterinsert']=$func;
	}

	/**
	*	Define oninsert trigger
	*	@return callback
	*	@param $func callback
	**/
	function oninsert($func) {
		return $this->afterinsert($func);
	}

	/**
	*	Define beforeupdate trigger
	*	@return callback
	*	@param $func callback
	**/
	function beforeupdate($func) {
		return $this->trigger['beforeupdate']=$func;
	}

	/**
	*	Define afterupdate trigger
	*	@return callback
	*	@param $func callback
	**/
	function afterupdate($func) {
		return $this->trigger['afterupdate']=$func;
	}

	/**
	*	Define onupdate trigger
	*	@return callback
	*	@param $func callback
	**/
	function onupdate($func) {
		return $this->afterupdate($func);
	}

	/**
	*	Define beforesave trigger
	*	@return callback
	*	@param $func callback
	**/
	function beforesave($func) {
		$this->trigger['beforeinsert']=$func;
		$this->trigger['beforeupdate']=$func;
		return $func;
	}

	/**
	*	Define aftersave trigger
	*	@return callback
	*	@param $func callback
	**/
	function aftersave($func) {
		$this->trigger['afterinsert']=$func;
		$this->trigger['afterupdate']=$func;
		return $func;
	}

	/**
	*	Define onsave trigger
	*	@return callback
	*	@param $func callback
	**/
	function onsave($func) {
		return $this->aftersave($func);
	}

	/**
	*	Define beforeerase trigger
	*	@return callback
	*	@param $func callback
	**/
	function beforeerase($func) {
		return $this->trigger['beforeerase']=$func;
	}

	/**
	*	Define aftererase trigger
	*	@return callback
	*	@param $func callback
	**/
	function aftererase($func) {
		return $this->trigger['aftererase']=$func;
	}

	/**
	*	Define onerase trigger
	*	@return callback
	*	@param $func callback
	**/
	function onerase($func) {
		return $this->aftererase($func);
	}

	/**
	*	Reset cursor
	*	@return NULL
	**/
	function reset() {
		$this->query=[];
		$this->ptr=0;
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/jig.php b/vendor/fatfree-3.6/lib/db/jig.php
new file mode 100755
index 0000000..79f33fb
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/jig.php
@@ -0,0 +1,175 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB;

//! In-memory/flat-file DB wrapper
class Jig {

	//@{ Storage formats
	const
		FORMAT_JSON=0,
		FORMAT_Serialized=1;
	//@}

	protected
		//! UUID
		$uuid,
		//! Storage location
		$dir,
		//! Current storage format
		$format,
		//! Jig log
		$log,
		//! Memory-held data
		$data,
		//! lazy load/save files
		$lazy;

	/**
	*	Read data from memory/file
	*	@return array
	*	@param $file string
	**/
	function &read($file) {
		if (!$this->dir || !is_file($dst=$this->dir.$file)) {
			if (!isset($this->data[$file]))
				$this->data[$file]=[];
			return $this->data[$file];
		}
		if ($this->lazy && isset($this->data[$file]))
			return $this->data[$file];
		$fw=\Base::instance();
		$raw=$fw->read($dst);
		switch ($this->format) {
			case self::FORMAT_JSON:
				$data=json_decode($raw,TRUE);
				break;
			case self::FORMAT_Serialized:
				$data=$fw->unserialize($raw);
				break;
		}
		$this->data[$file] = $data;
		return $this->data[$file];
	}

	/**
	*	Write data to memory/file
	*	@return int
	*	@param $file string
	*	@param $data array
	**/
	function write($file,array $data=NULL) {
		if (!$this->dir || $this->lazy)
			return count($this->data[$file]=$data);
		$fw=\Base::instance();
		switch ($this->format) {
			case self::FORMAT_JSON:
				$out=json_encode($data,JSON_PRETTY_PRINT);
				break;
			case self::FORMAT_Serialized:
				$out=$fw->serialize($data);
				break;
		}
		return $fw->write($this->dir.'/'.$file,$out);
	}

	/**
	*	Return directory
	*	@return string
	**/
	function dir() {
		return $this->dir;
	}

	/**
	*	Return UUID
	*	@return string
	**/
	function uuid() {
		return $this->uuid;
	}

	/**
	*	Return profiler results (or disable logging)
	*	@param $flag bool
	*	@return string
	**/
	function log($flag=TRUE) {
		if ($flag)
			return $this->log;
		$this->log=FALSE;
	}

	/**
	*	Jot down log entry
	*	@return NULL
	*	@param $frame string
	**/
	function jot($frame) {
		if ($frame)
			$this->log.=date('r').' '.$frame.PHP_EOL;
	}

	/**
	*	Clean storage
	*	@return NULL
	**/
	function drop() {
		if ($this->lazy) // intentional
			$this->data=[];
		if (!$this->dir)
			$this->data=[];
		elseif ($glob=@glob($this->dir.'/*',GLOB_NOSORT))
			foreach ($glob as $file)
				@unlink($file);
	}

	//! Prohibit cloning
	private function __clone() {
	}

	/**
	*	Instantiate class
	*	@param $dir string
	*	@param $format int
	**/
	function __construct($dir=NULL,$format=self::FORMAT_JSON,$lazy=FALSE) {
		if ($dir && !is_dir($dir))
			mkdir($dir,\Base::MODE,TRUE);
		$this->uuid=\Base::instance()->hash($this->dir=$dir);
		$this->format=$format;
		$this->lazy=$lazy;
	}

	/**
	*	save file on destruction
	**/
	function __destruct() {
		if ($this->lazy) {
			$this->lazy = FALSE;
			foreach ($this->data as $file => $data)
				$this->write($file,$data);
		}
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/jig/mapper.php b/vendor/fatfree-3.6/lib/db/jig/mapper.php
new file mode 100755
index 0000000..323f2c8
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/jig/mapper.php
@@ -0,0 +1,538 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB\Jig;

//! Flat-file DB mapper
class Mapper extends \DB\Cursor {

	protected
		//! Flat-file DB wrapper
		$db,
		//! Data file
		$file,
		//! Document identifier
		$id,
		//! Document contents
		$document=[],
		//! field map-reduce handlers
		$_reduce;

	/**
	*	Return database type
	*	@return string
	**/
	function dbtype() {
		return 'Jig';
	}

	/**
	*	Return TRUE if field is defined
	*	@return bool
	*	@param $key string
	**/
	function exists($key) {
		return array_key_exists($key,$this->document);
	}

	/**
	*	Assign value to field
	*	@return scalar|FALSE
	*	@param $key string
	*	@param $val scalar
	**/
	function set($key,$val) {
		return ($key=='_id')?FALSE:($this->document[$key]=$val);
	}

	/**
	*	Retrieve value of field
	*	@return scalar|FALSE
	*	@param $key string
	**/
	function &get($key) {
		if ($key=='_id')
			return $this->id;
		if (array_key_exists($key,$this->document))
			return $this->document[$key];
		user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
	}

	/**
	*	Delete field
	*	@return NULL
	*	@param $key string
	**/
	function clear($key) {
		if ($key!='_id')
			unset($this->document[$key]);
	}

	/**
	*	Convert array to mapper object
	*	@return object
	*	@param $id string
	*	@param $row array
	**/
	protected function factory($id,$row) {
		$mapper=clone($this);
		$mapper->reset();
		$mapper->id=$id;
		foreach ($row as $field=>$val)
			$mapper->document[$field]=$val;
		$mapper->query=[clone($mapper)];
		if (isset($mapper->trigger['load']))
			\Base::instance()->call($mapper->trigger['load'],$mapper);
		return $mapper;
	}

	/**
	*	Return fields of mapper object as an associative array
	*	@return array
	*	@param $obj object
	**/
	function cast($obj=NULL) {
		if (!$obj)
			$obj=$this;
		return $obj->document+['_id'=>$this->id];
	}

	/**
	*	Convert tokens in string expression to variable names
	*	@return string
	*	@param $str string
	**/
	function token($str) {
		$str=preg_replace_callback(
			'/(?<!\w)@(\w(?:[\w\.\[\]])*)/',
			function($token) {
				// Convert from JS dot notation to PHP array notation
				return '$'.preg_replace_callback(
					'/(\.\w+)|\[((?:[^\[\]]*|(?R))*)\]/',
					function($expr) {
						$fw=\Base::instance();
						return
							'['.
							($expr[1]?
								$fw->stringify(substr($expr[1],1)):
								(preg_match('/^\w+/',
									$mix=$this->token($expr[2]))?
									$fw->stringify($mix):
									$mix)).
							']';
					},
					$token[1]
				);
			},
			$str
		);
		return trim($str);
	}

	/**
	*	Return records that match criteria
	*	@return static[]|FALSE
	*	@param $filter array
	*	@param $options array
	*	@param $ttl int
	*	@param $log bool
	**/
	function find($filter=NULL,array $options=NULL,$ttl=0,$log=TRUE) {
		if (!$options)
			$options=[];
		$options+=[
			'order'=>NULL,
			'limit'=>0,
			'offset'=>0,
			'group'=>NULL,
		];
		$fw=\Base::instance();
		$cache=\Cache::instance();
		$db=$this->db;
		$now=microtime(TRUE);
		$data=[];
		if (!$fw->CACHE || !$ttl || !($cached=$cache->exists(
			$hash=$fw->hash($this->db->dir().
				$fw->stringify([$filter,$options])).'.jig',$data)) ||
			$cached[0]+$ttl<microtime(TRUE)) {
			$data=$db->read($this->file);
			if (is_null($data))
				return FALSE;
			foreach ($data as $id=>&$doc) {
				$doc['_id']=$id;
				unset($doc);
			}
			if ($filter) {
				if (!is_array($filter))
					return FALSE;
				// Normalize equality operator
				$expr=preg_replace('/(?<=[^<>!=])=(?!=)/','==',$filter[0]);
				// Prepare query arguments
				$args=isset($filter[1]) && is_array($filter[1])?
					$filter[1]:
					array_slice($filter,1,NULL,TRUE);
				$args=is_array($args)?$args:[1=>$args];
				$keys=$vals=[];
				$tokens=array_slice(
					token_get_all('<?php '.$this->token($expr)),1);
				$data=array_filter($data,
					function($_row) use($fw,$args,$tokens) {
						$_expr='';
						$ctr=0;
						$named=FALSE;
						foreach ($tokens as $token) {
							if (is_string($token))
								if ($token=='?') {
									// Positional
									$ctr++;
									$key=$ctr;
								}
								else {
									if ($token==':')
										$named=TRUE;
									else
										$_expr.=$token;
									continue;
								}
							elseif ($named &&
								token_name($token[0])=='T_STRING') {
								$key=':'.$token[1];
								$named=FALSE;
							}
							else {
								$_expr.=$token[1];
								continue;
							}
							$_expr.=$fw->stringify(
								is_string($args[$key])?
									addcslashes($args[$key],'\''):
									$args[$key]);
						}
						// Avoid conflict with user code
						unset($fw,$tokens,$args,$ctr,$token,$key,$named);
						extract($_row);
						// Evaluate pseudo-SQL expression
						return eval('return '.$_expr.';');
					}
				);
			}
			if (isset($options['group'])) {
				$cols=array_reverse($fw->split($options['group']));
				// sort into groups
				$data=$this->sort($data,$options['group']);
				foreach($data as $i=>&$row) {
					if (!isset($prev)) {
						$prev=$row;
						$prev_i=$i;
					}
					$drop=false;
					foreach ($cols as $col)
						if ($prev_i!=$i && array_key_exists($col,$row) &&
							array_key_exists($col,$prev) && $row[$col]==$prev[$col])
							// reduce/modify
							$drop=!isset($this->_reduce[$col]) || call_user_func_array(
								$this->_reduce[$col][0],[&$prev,&$row])!==FALSE;
						elseif (isset($this->_reduce[$col])) {
							$null=null;
							// initial
							call_user_func_array($this->_reduce[$col][0],[&$row,&$null]);
						}
					if ($drop)
						unset($data[$i]);
					else {
						$prev=&$row;
						$prev_i=$i;
					}
					unset($row);
				}
				// finalize
				if ($this->_reduce[$col][1])
					foreach($data as $i=>&$row) {
						$row=call_user_func($this->_reduce[$col][1],$row);
						if (!$row)
							unset($data[$i]);
						unset($row);
					}
			}
			if (isset($options['order']))
				$data=$this->sort($data,$options['order']);
			$data=array_slice($data,
				$options['offset'],$options['limit']?:NULL,TRUE);
			if ($fw->CACHE && $ttl)
				// Save to cache backend
				$cache->set($hash,$data,$ttl);
		}
		$out=[];
		foreach ($data as $id=>&$doc) {
			unset($doc['_id']);
			$out[]=$this->factory($id,$doc);
			unset($doc);
		}
		if ($log && isset($args)) {
			if ($filter)
				foreach ($args as $key=>$val) {
					$vals[]=$fw->stringify(is_array($val)?$val[0]:$val);
					$keys[]='/'.(is_numeric($key)?'\?':preg_quote($key)).'/';
				}
			$db->jot('('.sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
				$this->file.' [find] '.
				($filter?preg_replace($keys,$vals,$filter[0],1):''));
		}
		return $out;
	}

	/**
	*	Sort a collection
	*	@param $data
	*	@param $cond
	*	@return mixed
	*/
	protected function sort($data,$cond) {
		$cols=\Base::instance()->split($cond);
		uasort(
			$data,
			function($val1,$val2) use($cols) {
				foreach ($cols as $col) {
					$parts=explode(' ',$col,2);
					$order=empty($parts[1])?
						SORT_ASC:
						constant($parts[1]);
					$col=$parts[0];
					if (!array_key_exists($col,$val1))
						$val1[$col]=NULL;
					if (!array_key_exists($col,$val2))
						$val2[$col]=NULL;
					list($v1,$v2)=[$val1[$col],$val2[$col]];
					if ($out=strnatcmp($v1,$v2)*
						(($order==SORT_ASC)*2-1))
						return $out;
				}
				return 0;
			}
		);
		return $data;
	}

	/**
	*	Add reduce handler for grouped fields
	*	@param $key string
	*	@param $handler callback
	*	@param $finalize callback
	*/
	function reduce($key,$handler,$finalize=null){
		$this->_reduce[$key]=[$handler,$finalize];
	}

	/**
	*	Count records that match criteria
	*	@return int
	*	@param $filter array
	*	@param $options array
	*	@param $ttl int
	**/
	function count($filter=NULL,array $options=NULL,$ttl=0) {
		$now=microtime(TRUE);
		$out=count($this->find($filter,$options,$ttl,FALSE));
		$this->db->jot('('.sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
			$this->file.' [count] '.($filter?json_encode($filter):''));
		return $out;
	}

	/**
	*	Return record at specified offset using criteria of previous
	*	load() call and make it active
	*	@return array
	*	@param $ofs int
	**/
	function skip($ofs=1) {
		$this->document=($out=parent::skip($ofs))?$out->document:[];
		$this->id=$out?$out->id:NULL;
		if ($this->document && isset($this->trigger['load']))
			\Base::instance()->call($this->trigger['load'],$this);
		return $out;
	}

	/**
	*	Insert new record
	*	@return array
	**/
	function insert() {
		if ($this->id)
			return $this->update();
		$db=$this->db;
		$now=microtime(TRUE);
		while (($id=uniqid(NULL,TRUE)) &&
			($data=&$db->read($this->file)) && isset($data[$id]) &&
			!connection_aborted())
			usleep(mt_rand(0,100));
		$this->id=$id;
		$pkey=['_id'=>$this->id];
		if (isset($this->trigger['beforeinsert']) &&
			\Base::instance()->call($this->trigger['beforeinsert'],
				[$this,$pkey])===FALSE)
			return $this->document;
		$data[$id]=$this->document;
		$db->write($this->file,$data);
		$db->jot('('.sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
			$this->file.' [insert] '.json_encode($this->document));
		if (isset($this->trigger['afterinsert']))
			\Base::instance()->call($this->trigger['afterinsert'],
				[$this,$pkey]);
		$this->load(['@_id=?',$this->id]);
		return $this->document;
	}

	/**
	*	Update current record
	*	@return array
	**/
	function update() {
		$db=$this->db;
		$now=microtime(TRUE);
		$data=&$db->read($this->file);
		if (isset($this->trigger['beforeupdate']) &&
			\Base::instance()->call($this->trigger['beforeupdate'],
				[$this,['_id'=>$this->id]])===FALSE)
			return $this->document;
		$data[$this->id]=$this->document;
		$db->write($this->file,$data);
		$db->jot('('.sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
			$this->file.' [update] '.json_encode($this->document));
		if (isset($this->trigger['afterupdate']))
			\Base::instance()->call($this->trigger['afterupdate'],
				[$this,['_id'=>$this->id]]);
		return $this->document;
	}

	/**
	*	Delete current record
	*	@return bool
	*	@param $filter array
	*	@param $quick bool
	**/
	function erase($filter=NULL,$quick=FALSE) {
		$db=$this->db;
		$now=microtime(TRUE);
		$data=&$db->read($this->file);
		$pkey=['_id'=>$this->id];
		if ($filter) {
			foreach ($this->find($filter,NULL,FALSE) as $mapper)
				if (!$mapper->erase(null,$quick))
					return FALSE;
			return TRUE;
		}
		elseif (isset($this->id)) {
			unset($data[$this->id]);
			parent::erase();
		}
		else
			return FALSE;
		if (!$quick && isset($this->trigger['beforeerase']) &&
			\Base::instance()->call($this->trigger['beforeerase'],
				[$this,$pkey])===FALSE)
			return FALSE;
		$db->write($this->file,$data);
		if ($filter) {
			$args=isset($filter[1]) && is_array($filter[1])?
				$filter[1]:
				array_slice($filter,1,NULL,TRUE);
			$args=is_array($args)?$args:[1=>$args];
			foreach ($args as $key=>$val) {
				$vals[]=\Base::instance()->
					stringify(is_array($val)?$val[0]:$val);
				$keys[]='/'.(is_numeric($key)?'\?':preg_quote($key)).'/';
			}
		}
		$db->jot('('.sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
			$this->file.' [erase] '.
			($filter?preg_replace($keys,$vals,$filter[0],1):''));
		if (!$quick && isset($this->trigger['aftererase']))
			\Base::instance()->call($this->trigger['aftererase'],
				[$this,$pkey]);
		return TRUE;
	}

	/**
	*	Reset cursor
	*	@return NULL
	**/
	function reset() {
		$this->id=NULL;
		$this->document=[];
		parent::reset();
	}

	/**
	*	Hydrate mapper object using hive array variable
	*	@return NULL
	*	@param $var array|string
	*	@param $func callback
	**/
	function copyfrom($var,$func=NULL) {
		if (is_string($var))
			$var=\Base::instance()->$var;
		if ($func)
			$var=call_user_func($func,$var);
		foreach ($var as $key=>$val)
			$this->set($key,$val);
	}

	/**
	*	Populate hive array variable with mapper fields
	*	@return NULL
	*	@param $key string
	**/
	function copyto($key) {
		$var=&\Base::instance()->ref($key);
		foreach ($this->document as $key=>$field)
			$var[$key]=$field;
	}

	/**
	*	Return field names
	*	@return array
	**/
	function fields() {
		return array_keys($this->document);
	}

	/**
	*	Retrieve external iterator for fields
	*	@return object
	**/
	function getiterator() {
		return new \ArrayIterator($this->cast());
	}

	/**
	*	Instantiate class
	*	@return void
	*	@param $db object
	*	@param $file string
	**/
	function __construct(\DB\Jig $db,$file) {
		$this->db=$db;
		$this->file=$file;
		$this->reset();
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/jig/session.php b/vendor/fatfree-3.6/lib/db/jig/session.php
new file mode 100755
index 0000000..e7fecb3
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/jig/session.php
@@ -0,0 +1,190 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB\Jig;

//! Jig-managed session handler
class Session extends Mapper {

	protected
		//! Session ID
		$sid,
		//! Anti-CSRF token
		$_csrf,
		//! User agent
		$_agent,
		//! IP,
		$_ip,
		//! Suspect callback
		$onsuspect;

	/**
	*	Open session
	*	@return TRUE
	*	@param $path string
	*	@param $name string
	**/
	function open($path,$name) {
		return TRUE;
	}

	/**
	*	Close session
	*	@return TRUE
	**/
	function close() {
		$this->reset();
		$this->sid=NULL;
		return TRUE;
	}

	/**
	*	Return session data in serialized format
	*	@return string
	*	@param $id string
	**/
	function read($id) {
		$this->load(['@session_id=?',$this->sid=$id]);
		if ($this->dry())
			return '';
		if ($this->get('ip')!=$this->_ip || $this->get('agent')!=$this->_agent) {
			$fw=\Base::instance();
			if (!isset($this->onsuspect) ||
				$fw->call($this->onsuspect,[$this,$id])===FALSE) {
				// NB: `session_destroy` can't be called at that stage;
				// `session_start` not completed
				$this->destroy($id);
				$this->close();
				unset($fw->{'COOKIE.'.session_name()});
				$fw->error(403);
			}
		}
		return $this->get('data');
	}

	/**
	*	Write session data
	*	@return TRUE
	*	@param $id string
	*	@param $data string
	**/
	function write($id,$data) {
		$this->set('session_id',$id);
		$this->set('data',$data);
		$this->set('ip',$this->_ip);
		$this->set('agent',$this->_agent);
		$this->set('stamp',time());
		$this->save();
		return TRUE;
	}

	/**
	*	Destroy session
	*	@return TRUE
	*	@param $id string
	**/
	function destroy($id) {
		$this->erase(['@session_id=?',$id]);
		return TRUE;
	}

	/**
	*	Garbage collector
	*	@return TRUE
	*	@param $max int
	**/
	function cleanup($max) {
		$this->erase(['@stamp+?<?',$max,time()]);
		return TRUE;
	}

	/**
	 *	Return session id (if session has started)
	 *	@return string|NULL
	 **/
	function sid() {
		return $this->sid;
	}

	/**
	 *	Return anti-CSRF token
	 *	@return string
	 **/
	function csrf() {
		return $this->_csrf;
	}

	/**
	 *	Return IP address
	 *	@return string
	 **/
	function ip() {
		return $this->_ip;
	}

	/**
	*	Return Unix timestamp
	*	@return string|FALSE
	**/
	function stamp() {
		if (!$this->sid)
			session_start();
		return $this->dry()?FALSE:$this->get('stamp');
	}

	/**
	*	Return HTTP user agent
	*	@return string|FALSE
	**/
	function agent() {
		return $this->_agent;
	}

	/**
	*	Instantiate class
	*	@param $db \DB\Jig
	*	@param $file string
	*	@param $onsuspect callback
	*	@param $key string
	**/
	function __construct(\DB\Jig $db,$file='sessions',$onsuspect=NULL,$key=NULL) {
		parent::__construct($db,$file);
		$this->onsuspect=$onsuspect;
		session_set_save_handler(
			[$this,'open'],
			[$this,'close'],
			[$this,'read'],
			[$this,'write'],
			[$this,'destroy'],
			[$this,'cleanup']
		);
		register_shutdown_function('session_commit');
		$fw=\Base::instance();
		$headers=$fw->HEADERS;
		$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
		if ($key)
			$fw->$key=$this->_csrf;
		$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
		$this->_ip=$fw->IP;
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/mongo.php b/vendor/fatfree-3.6/lib/db/mongo.php
new file mode 100755
index 0000000..86ac184
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/mongo.php
@@ -0,0 +1,145 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB;

//! MongoDB wrapper
class Mongo {

	//@{
	const
		E_Profiler='MongoDB profiler is disabled';
	//@}

	protected
		//! UUID
		$uuid,
		//! Data source name
		$dsn,
		//! MongoDB object
		$db,
		//! Legacy flag
		$legacy,
		//! MongoDB log
		$log;

	/**
	*	Return data source name
	*	@return string
	**/
	function dsn() {
		return $this->dsn;
	}

	/**
	*	Return UUID
	*	@return string
	**/
	function uuid() {
		return $this->uuid;
	}

	/**
	*	Return MongoDB profiler results (or disable logging)
	*	@param $flag bool
	*	@return string
	**/
	function log($flag=TRUE) {
		if ($flag) {
			$cursor=$this->db->selectcollection('system.profile')->find();
			foreach (iterator_to_array($cursor) as $frame)
				if (!preg_match('/\.system\..+$/',$frame['ns']))
					$this->log.=date('r',$this->legacy() ?
						$frame['ts']->sec : (round((string)$frame['ts'])/1000)).
						' ('.sprintf('%.1f',$frame['millis']).'ms) '.
						$frame['ns'].' ['.$frame['op'].'] '.
						(empty($frame['query'])?
							'':json_encode($frame['query'])).
						(empty($frame['command'])?
							'':json_encode($frame['command'])).
						PHP_EOL;
		} else {
			$this->log=FALSE;
			if ($this->legacy)
				$this->db->setprofilinglevel(-1);
			else
				$this->db->command(['profile'=>-1]);
		}
		return $this->log;
	}

	/**
	*	Intercept native call to re-enable profiler
	*	@return int
	**/
	function drop() {
		$out=$this->db->drop();
		if ($this->log!==FALSE) {
			if ($this->legacy)
				$this->db->setprofilinglevel(2);
			else
				$this->db->command(['profile'=>2]);
		}
		return $out;
	}

	/**
	*	Redirect call to MongoDB object
	*	@return mixed
	*	@param $func string
	*	@param $args array
	**/
	function __call($func,array $args) {
		return call_user_func_array([$this->db,$func],$args);
	}

	/**
	*	Return TRUE if legacy driver is loaded
	*	@return bool
	**/
	function legacy() {
		return $this->legacy;
	}

	//! Prohibit cloning
	private function __clone() {
	}

	/**
	*	Instantiate class
	*	@param $dsn string
	*	@param $dbname string
	*	@param $options array
	**/
	function __construct($dsn,$dbname,array $options=NULL) {
		$this->uuid=\Base::instance()->hash($this->dsn=$dsn);
		if ($this->legacy=class_exists('\MongoClient')) {
			$this->db=new \MongoDB(new \MongoClient($dsn,$options?:[]),$dbname);
			$this->db->setprofilinglevel(2);
		}
		else {
			$this->db=(new \MongoDB\Client($dsn,$options?:[]))->$dbname;
			$this->db->command(['profile'=>2]);
		}
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/mongo/mapper.php b/vendor/fatfree-3.6/lib/db/mongo/mapper.php
new file mode 100755
index 0000000..f038b6c
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/mongo/mapper.php
@@ -0,0 +1,399 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB\Mongo;

//! MongoDB mapper
class Mapper extends \DB\Cursor {

	protected
		//! MongoDB wrapper
		$db,
		//! Legacy flag
		$legacy,
		//! Mongo collection
		$collection,
		//! Mongo document
		$document=[],
		//! Mongo cursor
		$cursor,
		//! Defined fields
		$fields;

	/**
	*	Return database type
	*	@return string
	**/
	function dbtype() {
		return 'Mongo';
	}

	/**
	*	Return TRUE if field is defined
	*	@return bool
	*	@param $key string
	**/
	function exists($key) {
		return array_key_exists($key,$this->document);
	}

	/**
	*	Assign value to field
	*	@return scalar|FALSE
	*	@param $key string
	*	@param $val scalar
	**/
	function set($key,$val) {
		return $this->document[$key]=$val;
	}

	/**
	*	Retrieve value of field
	*	@return scalar|FALSE
	*	@param $key string
	**/
	function &get($key) {
		if ($this->exists($key))
			return $this->document[$key];
		user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
	}

	/**
	*	Delete field
	*	@return NULL
	*	@param $key string
	**/
	function clear($key) {
		unset($this->document[$key]);
	}

	/**
	*	Convert array to mapper object
	*	@return static
	*	@param $row array
	**/
	protected function factory($row) {
		$mapper=clone($this);
		$mapper->reset();
		foreach ($row as $key=>$val)
			$mapper->document[$key]=$val;
		$mapper->query=[clone($mapper)];
		if (isset($mapper->trigger['load']))
			\Base::instance()->call($mapper->trigger['load'],$mapper);
		return $mapper;
	}

	/**
	*	Return fields of mapper object as an associative array
	*	@return array
	*	@param $obj object
	**/
	function cast($obj=NULL) {
		if (!$obj)
			$obj=$this;
		return $obj->document;
	}

	/**
	*	Build query and execute
	*	@return static[]
	*	@param $fields string
	*	@param $filter array
	*	@param $options array
	*	@param $ttl int
	**/
	function select($fields=NULL,$filter=NULL,array $options=NULL,$ttl=0) {
		if (!$options)
			$options=[];
		$options+=[
			'group'=>NULL,
			'order'=>NULL,
			'limit'=>0,
			'offset'=>0
		];
		$fw=\Base::instance();
		$cache=\Cache::instance();
		if (!($cached=$cache->exists($hash=$fw->hash($this->db->dsn().
			$fw->stringify([$fields,$filter,$options])).'.mongo',
			$result)) || !$ttl || $cached[0]+$ttl<microtime(TRUE)) {
			if ($options['group']) {
				$grp=$this->collection->group(
					$options['group']['keys'],
					$options['group']['initial'],
					$options['group']['reduce'],
					[
						'condition'=>$filter,
						'finalize'=>$options['group']['finalize']
					]
				);
				$tmp=$this->db->selectcollection(
					$fw->HOST.'.'.$fw->BASE.'.'.
					uniqid(NULL,TRUE).'.tmp'
				);
				$tmp->batchinsert($grp['retval'],['w'=>1]);
				$filter=[];
				$collection=$tmp;
			}
			else {
				$filter=$filter?:[];
				$collection=$this->collection;
			}
			if ($this->legacy) {
				$this->cursor=$collection->find($filter,$fields?:[]);
				if ($options['order'])
					$this->cursor=$this->cursor->sort($options['order']);
				if ($options['limit'])
					$this->cursor=$this->cursor->limit($options['limit']);
				if ($options['offset'])
					$this->cursor=$this->cursor->skip($options['offset']);
				$result=[];
				while ($this->cursor->hasnext())
					$result[]=$this->cursor->getnext();
			}
			else {
				$this->cursor=$collection->find($filter,[
					'sort'=>$options['order'],
					'limit'=>$options['limit'],
					'skip'=>$options['offset']
				]);
				$result=$this->cursor->toarray();
			}
			if ($options['group'])
				$tmp->drop();
			if ($fw->CACHE && $ttl)
				// Save to cache backend
				$cache->set($hash,$result,$ttl);
		}
		$out=[];
		foreach ($result as $doc)
			$out[]=$this->factory($doc);
		return $out;
	}

	/**
	*	Return records that match criteria
	*	@return static[]
	*	@param $filter array
	*	@param $options array
	*	@param $ttl int
	**/
	function find($filter=NULL,array $options=NULL,$ttl=0) {
		if (!$options)
			$options=[];
		$options+=[
			'group'=>NULL,
			'order'=>NULL,
			'limit'=>0,
			'offset'=>0
		];
		return $this->select($this->fields,$filter,$options,$ttl);
	}

	/**
	*	Count records that match criteria
	*	@return int
	*	@param $filter array
	*	@param $options array
	*	@param $ttl int
	**/
	function count($filter=NULL,array $options=NULL,$ttl=0) {
		$fw=\Base::instance();
		$cache=\Cache::instance();
		if (!($cached=$cache->exists($hash=$fw->hash($fw->stringify(
			[$filter])).'.mongo',$result)) || !$ttl ||
			$cached[0]+$ttl<microtime(TRUE)) {
			$result=$this->collection->count($filter?:[]);
			if ($fw->CACHE && $ttl)
				// Save to cache backend
				$cache->set($hash,$result,$ttl);
		}
		return $result;
	}

	/**
	*	Return record at specified offset using criteria of previous
	*	load() call and make it active
	*	@return array
	*	@param $ofs int
	**/
	function skip($ofs=1) {
		$this->document=($out=parent::skip($ofs))?$out->document:[];
		if ($this->document && isset($this->trigger['load']))
			\Base::instance()->call($this->trigger['load'],$this);
		return $out;
	}

	/**
	*	Insert new record
	*	@return array
	**/
	function insert() {
		if (isset($this->document['_id']))
			return $this->update();
		if (isset($this->trigger['beforeinsert']) &&
			\Base::instance()->call($this->trigger['beforeinsert'],
				[$this,['_id'=>$this->document['_id']]])===FALSE)
			return $this->document;
		if ($this->legacy) {
			$this->collection->insert($this->document);
			$pkey=['_id'=>$this->document['_id']];
		}
		else {
			$result=$this->collection->insertone($this->document);
			$pkey=['_id'=>$result->getinsertedid()];
		}
		if (isset($this->trigger['afterinsert']))
			\Base::instance()->call($this->trigger['afterinsert'],
				[$this,$pkey]);
		$this->load($pkey);
		return $this->document;
	}

	/**
	*	Update current record
	*	@return array
	**/
	function update() {
		$pkey=['_id'=>$this->document['_id']];
		if (isset($this->trigger['beforeupdate']) &&
			\Base::instance()->call($this->trigger['beforeupdate'],
				[$this,$pkey])===FALSE)
			return $this->document;
		$upsert=['upsert'=>TRUE];
		if ($this->legacy)
			$this->collection->update($pkey,$this->document,$upsert);
		else
			$this->collection->replaceone($pkey,$this->document,$upsert);
		if (isset($this->trigger['afterupdate']))
			\Base::instance()->call($this->trigger['afterupdate'],
				[$this,$pkey]);
		return $this->document;
	}

	/**
	*	Delete current record
	*	@return bool
	*	@param $quick bool
	*	@param $filter array
	**/
	function erase($filter=NULL,$quick=TRUE) {
		if ($filter) {
			if (!$quick) {
				foreach ($this->find($filter) as $mapper)
					if (!$mapper->erase())
						return FALSE;
				return TRUE;
			}
			return $this->legacy?
				$this->collection->remove($filter):
				$this->collection->deletemany($filter);
		}
		$pkey=['_id'=>$this->document['_id']];
		if (isset($this->trigger['beforeerase']) &&
			\Base::instance()->call($this->trigger['beforeerase'],
				[$this,$pkey])===FALSE)
			return FALSE;
		$result=$this->legacy?
			$this->collection->remove(['_id'=>$this->document['_id']]):
			$this->collection->deleteone(['_id'=>$this->document['_id']]);
		parent::erase();
		if (isset($this->trigger['aftererase']))
			\Base::instance()->call($this->trigger['aftererase'],
				[$this,$pkey]);
		return $result;
	}

	/**
	*	Reset cursor
	*	@return NULL
	**/
	function reset() {
		$this->document=[];
		parent::reset();
	}

	/**
	*	Hydrate mapper object using hive array variable
	*	@return NULL
	*	@param $var array|string
	*	@param $func callback
	**/
	function copyfrom($var,$func=NULL) {
		if (is_string($var))
			$var=\Base::instance()->$var;
		if ($func)
			$var=call_user_func($func,$var);
		foreach ($var as $key=>$val)
			$this->set($key,$val);
	}

	/**
	*	Populate hive array variable with mapper fields
	*	@return NULL
	*	@param $key string
	**/
	function copyto($key) {
		$var=&\Base::instance()->ref($key);
		foreach ($this->document as $key=>$field)
			$var[$key]=$field;
	}

	/**
	*	Return field names
	*	@return array
	**/
	function fields() {
		return array_keys($this->document);
	}

	/**
	*	Return the cursor from last query
	*	@return object|NULL
	**/
	function cursor() {
		return $this->cursor;
	}

	/**
	*	Retrieve external iterator for fields
	*	@return object
	**/
	function getiterator() {
		return new \ArrayIterator($this->cast());
	}

	/**
	*	Instantiate class
	*	@return void
	*	@param $db object
	*	@param $collection string
	*	@param $fields array
	**/
	function __construct(\DB\Mongo $db,$collection,$fields=NULL) {
		$this->db=$db;
		$this->legacy=$db->legacy();
		$this->collection=$db->selectcollection($collection);
		$this->fields=$fields;
		$this->reset();
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/mongo/session.php b/vendor/fatfree-3.6/lib/db/mongo/session.php
new file mode 100755
index 0000000..98bafd6
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/mongo/session.php
@@ -0,0 +1,190 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB\Mongo;

//! MongoDB-managed session handler
class Session extends Mapper {

	protected
		//! Session ID
		$sid,
		//! Anti-CSRF token
		$_csrf,
		//! User agent
		$_agent,
		//! IP,
		$_ip,
		//! Suspect callback
		$onsuspect;

	/**
	*	Open session
	*	@return TRUE
	*	@param $path string
	*	@param $name string
	**/
	function open($path,$name) {
		return TRUE;
	}

	/**
	*	Close session
	*	@return TRUE
	**/
	function close() {
		$this->reset();
		$this->sid=NULL;
		return TRUE;
	}

	/**
	*	Return session data in serialized format
	*	@return string
	*	@param $id string
	**/
	function read($id) {
		$this->load(['session_id'=>$this->sid=$id]);
		if ($this->dry())
			return '';
		if ($this->get('ip')!=$this->_ip || $this->get('agent')!=$this->_agent) {
			$fw=\Base::instance();
			if (!isset($this->onsuspect) ||
				$fw->call($this->onsuspect,[$this,$id])===FALSE) {
				// NB: `session_destroy` can't be called at that stage;
				// `session_start` not completed
				$this->destroy($id);
				$this->close();
				unset($fw->{'COOKIE.'.session_name()});
				$fw->error(403);
			}
		}
		return $this->get('data');
	}

	/**
	*	Write session data
	*	@return TRUE
	*	@param $id string
	*	@param $data string
	**/
	function write($id,$data) {
		$this->set('session_id',$id);
		$this->set('data',$data);
		$this->set('ip',$this->_ip);
		$this->set('agent',$this->_agent);
		$this->set('stamp',time());
		$this->save();
		return TRUE;
	}

	/**
	*	Destroy session
	*	@return TRUE
	*	@param $id string
	**/
	function destroy($id) {
		$this->erase(['session_id'=>$id]);
		return TRUE;
	}

	/**
	*	Garbage collector
	*	@return TRUE
	*	@param $max int
	**/
	function cleanup($max) {
		$this->erase(['$where'=>'this.stamp+'.$max.'<'.time()]);
		return TRUE;
	}

	/**
	 *	Return session id (if session has started)
	 *	@return string|NULL
	 **/
	function sid() {
		return $this->sid;
	}

	/**
	 *	Return anti-CSRF token
	 *	@return string
	 **/
	function csrf() {
		return $this->_csrf;
	}

	/**
	 *	Return IP address
	 *	@return string
	 **/
	function ip() {
		return $this->_ip;
	}

	/**
	 *	Return Unix timestamp
	 *	@return string|FALSE
	 **/
	function stamp() {
		if (!$this->sid)
			session_start();
		return $this->dry()?FALSE:$this->get('stamp');
	}

	/**
	 *	Return HTTP user agent
	 *	@return string
	 **/
	function agent() {
		return $this->_agent;
	}

	/**
	*	Instantiate class
	*	@param $db \DB\Mongo
	*	@param $table string
	*	@param $onsuspect callback
	*	@param $key string
	**/
	function __construct(\DB\Mongo $db,$table='sessions',$onsuspect=NULL,$key=NULL) {
		parent::__construct($db,$table);
		$this->onsuspect=$onsuspect;
		session_set_save_handler(
			[$this,'open'],
			[$this,'close'],
			[$this,'read'],
			[$this,'write'],
			[$this,'destroy'],
			[$this,'cleanup']
		);
		register_shutdown_function('session_commit');
		$fw=\Base::instance();
		$headers=$fw->HEADERS;
		$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
		if ($key)
			$fw->$key=$this->_csrf;
		$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
		$this->_ip=$fw->IP;
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/sql.php b/vendor/fatfree-3.6/lib/db/sql.php
new file mode 100755
index 0000000..39e77f0
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/sql.php
@@ -0,0 +1,523 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB;

//! PDO wrapper
class SQL {

	//@{ Error messages
	const
		E_PKey='Table %s does not have a primary key';
	//@}

	const
		PARAM_FLOAT='float';

	protected
		//! UUID
		$uuid,
		//! Raw PDO
		$pdo,
		//! Data source name
		$dsn,
		//! Database engine
		$engine,
		//! Database name
		$dbname,
		//! Transaction flag
		$trans=FALSE,
		//! Number of rows affected by query
		$rows=0,
		//! SQL log
		$log;

	/**
	*	Begin SQL transaction
	*	@return bool
	**/
	function begin() {
		$out=$this->pdo->begintransaction();
		$this->trans=TRUE;
		return $out;
	}

	/**
	*	Rollback SQL transaction
	*	@return bool
	**/
	function rollback() {
		$out=$this->pdo->rollback();
		$this->trans=FALSE;
		return $out;
	}

	/**
	*	Commit SQL transaction
	*	@return bool
	**/
	function commit() {
		$out=$this->pdo->commit();
		$this->trans=FALSE;
		return $out;
	}

	/**
	*	Return transaction flag
	*	@return bool
	**/
	function trans() {
		return $this->trans;
	}

	/**
	*	Map data type of argument to a PDO constant
	*	@return int
	*	@param $val scalar
	**/
	function type($val) {
		switch (gettype($val)) {
			case 'NULL':
				return \PDO::PARAM_NULL;
			case 'boolean':
				return \PDO::PARAM_BOOL;
			case 'integer':
				return \PDO::PARAM_INT;
			case 'resource':
				return \PDO::PARAM_LOB;
			case 'float':
				return self::PARAM_FLOAT;
			default:
				return \PDO::PARAM_STR;
		}
	}

	/**
	*	Cast value to PHP type
	*	@return mixed
	*	@param $type string
	*	@param $val mixed
	**/
	function value($type,$val) {
		switch ($type) {
			case self::PARAM_FLOAT:
				if (!is_string($val))
					$val=str_replace(',','.',$val);
				return $val;
			case \PDO::PARAM_NULL:
				return NULL;
			case \PDO::PARAM_INT:
				return (int)$val;
			case \PDO::PARAM_BOOL:
				return (bool)$val;
			case \PDO::PARAM_STR:
				return (string)$val;
			case \PDO::PARAM_LOB:
				return (binary)$val;
		}
	}

	/**
	*	Execute SQL statement(s)
	*	@return array|int|FALSE
	*	@param $cmds string|array
	*	@param $args string|array
	*	@param $ttl int|array
	*	@param $log bool
	*	@param $stamp bool
	**/
	function exec($cmds,$args=NULL,$ttl=0,$log=TRUE,$stamp=FALSE) {
		$tag='';
		if (is_array($ttl))
			list($ttl,$tag)=$ttl;
		$auto=FALSE;
		if (is_null($args))
			$args=[];
		elseif (is_scalar($args))
			$args=[1=>$args];
		if (is_array($cmds)) {
			if (count($args)<($count=count($cmds)))
				// Apply arguments to SQL commands
				$args=array_fill(0,$count,$args);
			if (!$this->trans) {
				$this->begin();
				$auto=TRUE;
			}
		}
		else {
			$count=1;
			$cmds=[$cmds];
			$args=[$args];
		}
		if ($this->log===FALSE)
			$log=FALSE;
		$fw=\Base::instance();
		$cache=\Cache::instance();
		$result=FALSE;
		for ($i=0;$i<$count;$i++) {
			$cmd=$cmds[$i];
			$arg=$args[$i];
			// ensure 1-based arguments
			if (array_key_exists(0,$arg)) {
				array_unshift($arg,'');
				unset($arg[0]);
			}
			if (!preg_replace('/(^\s+|[\s;]+$)/','',$cmd))
				continue;
			$now=microtime(TRUE);
			$keys=$vals=[];
			if ($fw->CACHE && $ttl && ($cached=$cache->exists(
				$hash=$fw->hash($this->dsn.$cmd.
				$fw->stringify($arg)).($tag?'.'.$tag:'').'.sql',$result)) &&
				$cached[0]+$ttl>microtime(TRUE)) {
				foreach ($arg as $key=>$val) {
					$vals[]=$fw->stringify(is_array($val)?$val[0]:$val);
					$keys[]='/'.preg_quote(is_numeric($key)?chr(0).'?':$key).
						'/';
				}
				if ($log)
					$this->log.=($stamp?(date('r').' '):'').'('.
						sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
						'[CACHED] '.
						preg_replace($keys,$vals,
							str_replace('?',chr(0).'?',$cmd),1).PHP_EOL;
			}
			elseif (is_object($query=$this->pdo->prepare($cmd))) {
				foreach ($arg as $key=>$val) {
					if (is_array($val)) {
						// User-specified data type
						$query->bindvalue($key,$val[0],
							$val[1]==self::PARAM_FLOAT?\PDO::PARAM_STR:$val[1]);
						$vals[]=$fw->stringify($this->value($val[1],$val[0]));
					}
					else {
						// Convert to PDO data type
						$query->bindvalue($key,$val,
							($type=$this->type($val))==self::PARAM_FLOAT?
								\PDO::PARAM_STR:$type);
						$vals[]=$fw->stringify($this->value($type,$val));
					}
					$keys[]='/'.preg_quote(is_numeric($key)?chr(0).'?':$key).
						'/';
				}
				if ($log)
					$this->log.=($stamp?(date('r').' '):'').' (-0ms) '.
						preg_replace($keys,$vals,
							str_replace('?',chr(0).'?',$cmd),1).PHP_EOL;
				$query->execute();
				if ($log)
					$this->log=str_replace('(-0ms)',
						'('.sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms)',
						$this->log);
				if (($error=$query->errorinfo()) && $error[0]!=\PDO::ERR_NONE) {
					// Statement-level error occurred
					if ($this->trans)
						$this->rollback();
					user_error('PDOStatement: '.$error[2],E_USER_ERROR);
				}
				if (preg_match('/(?:^[\s\(]*'.
					'(?:EXPLAIN|SELECT|PRAGMA|SHOW)|RETURNING)\b/is',$cmd) ||
					(preg_match('/^\s*(?:CALL|EXEC)\b/is',$cmd) &&
						$query->columnCount())) {
					$result=$query->fetchall(\PDO::FETCH_ASSOC);
					// Work around SQLite quote bug
					if (preg_match('/sqlite2?/',$this->engine))
						foreach ($result as $pos=>$rec) {
							unset($result[$pos]);
							$result[$pos]=[];
							foreach ($rec as $key=>$val)
								$result[$pos][trim($key,'\'"[]`')]=$val;
						}
					$this->rows=count($result);
					if ($fw->CACHE && $ttl)
						// Save to cache backend
						$cache->set($hash,$result,$ttl);
				}
				else
					$this->rows=$result=$query->rowcount();
				$query->closecursor();
				unset($query);
			}
			elseif (($error=$this->errorinfo()) && $error[0]!=\PDO::ERR_NONE) {
				// PDO-level error occurred
				if ($this->trans)
					$this->rollback();
				user_error('PDO: '.$error[2],E_USER_ERROR);
			}

		}
		if ($this->trans && $auto)
			$this->commit();
		return $result;
	}

	/**
	*	Return number of rows affected by last query
	*	@return int
	**/
	function count() {
		return $this->rows;
	}

	/**
	*	Return SQL profiler results (or disable logging)
	*	@return string
	*	@param $flag bool
	**/
	function log($flag=TRUE) {
		if ($flag)
			return $this->log;
		$this->log=FALSE;
	}

	/**
	*	Return TRUE if table exists
	*	@return bool
	*	@param $table string
	**/
	function exists($table) {
		$mode=$this->pdo->getAttribute(\PDO::ATTR_ERRMODE);
		$this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_SILENT);
		$out=$this->pdo->
			query('SELECT 1 FROM '.$this->quotekey($table).' LIMIT 1');
		$this->pdo->setAttribute(\PDO::ATTR_ERRMODE,$mode);
		return is_object($out);
	}

	/**
	*	Retrieve schema of SQL table
	*	@return array|FALSE
	*	@param $table string
	*	@param $fields array|string
	*	@param $ttl int|array
	**/
	function schema($table,$fields=NULL,$ttl=0) {
		$fw=\Base::instance();
		$cache=\Cache::instance();
		if ($fw->CACHE && $ttl &&
			($cached=$cache->exists(
				$hash=$fw->hash($this->dsn.$table).'.schema',$result)) &&
			$cached[0]+$ttl>microtime(TRUE))
			return $result;
		if (strpos($table,'.'))
			list($schema,$table)=explode('.',$table);
		// Supported engines
		$cmd=[
			'sqlite2?'=>[
				'PRAGMA table_info(`'.$table.'`)',
				'name','type','dflt_value','notnull',0,'pk',TRUE],
			'mysql'=>[
				'SHOW columns FROM `'.$this->dbname.'`.`'.$table.'`',
				'Field','Type','Default','Null','YES','Key','PRI'],
			'mssql|sqlsrv|sybase|dblib|pgsql|odbc'=>[
				'SELECT '.
					'C.COLUMN_NAME AS field,'.
					'C.DATA_TYPE AS type,'.
					'C.COLUMN_DEFAULT AS defval,'.
					'C.IS_NULLABLE AS nullable,'.
					'T.CONSTRAINT_TYPE AS pkey '.
				'FROM INFORMATION_SCHEMA.COLUMNS AS C '.
				'LEFT OUTER JOIN '.
					'INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS K '.
					'ON '.
						'C.TABLE_NAME=K.TABLE_NAME AND '.
						'C.COLUMN_NAME=K.COLUMN_NAME AND '.
						'C.TABLE_SCHEMA=K.TABLE_SCHEMA '.
						($this->dbname?
							('AND C.TABLE_CATALOG=K.TABLE_CATALOG '):'').
				'LEFT OUTER JOIN '.
					'INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS T ON '.
						'K.TABLE_NAME=T.TABLE_NAME AND '.
						'K.CONSTRAINT_NAME=T.CONSTRAINT_NAME AND '.
						'K.TABLE_SCHEMA=T.TABLE_SCHEMA '.
						($this->dbname?
							('AND K.TABLE_CATALOG=T.TABLE_CATALOG '):'').
				'WHERE '.
					'C.TABLE_NAME='.$this->quote($table).
					($this->dbname?
						(' AND C.TABLE_CATALOG='.
							$this->quote($this->dbname)):''),
				'field','type','defval','nullable','YES','pkey','PRIMARY KEY'],
			'oci'=>[
				'SELECT c.column_name AS field, '.
					'c.data_type AS type, '.
					'c.data_default AS defval, '.
					'c.nullable AS nullable, '.
					'(SELECT t.constraint_type '.
						'FROM all_cons_columns acc '.
						'LEFT OUTER JOIN all_constraints t '.
						'ON acc.constraint_name=t.constraint_name '.
						'WHERE acc.table_name='.$this->quote($table).' '.
						'AND acc.column_name=c.column_name '.
						'AND constraint_type='.$this->quote('P').') AS pkey '.
				'FROM all_tab_cols c '.
				'WHERE c.table_name='.$this->quote($table),
				'FIELD','TYPE','DEFVAL','NULLABLE','Y','PKEY','P']
		];
		if (is_string($fields))
			$fields=\Base::instance()->split($fields);
		$conv=[
			'int\b|integer'=>\PDO::PARAM_INT,
			'bool'=>\PDO::PARAM_BOOL,
			'blob|bytea|image|binary'=>\PDO::PARAM_LOB,
			'float|real|double|decimal|numeric'=>self::PARAM_FLOAT,
			'.+'=>\PDO::PARAM_STR
		];
		foreach ($cmd as $key=>$val)
			if (preg_match('/'.$key.'/',$this->engine)) {
				$rows=[];
				foreach ($this->exec($val[0],NULL) as $row)
					if (!$fields || in_array($row[$val[1]],$fields)) {
						foreach ($conv as $regex=>$type)
							if (preg_match('/'.$regex.'/i',$row[$val[2]]))
								break;
						$rows[$row[$val[1]]]=[
							'type'=>$row[$val[2]],
							'pdo_type'=>$type,
							'default'=>is_string($row[$val[3]])?
								preg_replace('/^\s*([\'"])(.*)\1\s*/','\2',
								$row[$val[3]]):$row[$val[3]],
							'nullable'=>$row[$val[4]]==$val[5],
							'pkey'=>$row[$val[6]]==$val[7]
						];
					}
				if ($fw->CACHE && $ttl)
					// Save to cache backend
					$cache->set($hash,$rows,$ttl);
				return $rows;
			}
		user_error(sprintf(self::E_PKey,$table),E_USER_ERROR);
		return FALSE;
	}

	/**
	*	Quote string
	*	@return string
	*	@param $val mixed
	*	@param $type int
	**/
	function quote($val,$type=\PDO::PARAM_STR) {
		return $this->engine=='odbc'?
			(is_string($val)?
				\Base::instance()->stringify(str_replace('\'','\'\'',$val)):
				$val):
			$this->pdo->quote($val,$type);
	}

	/**
	*	Return UUID
	*	@return string
	**/
	function uuid() {
		return $this->uuid;
	}

	/**
	*	Return parent object
	*	@return \PDO
	**/
	function pdo() {
		return $this->pdo;
	}

	/**
	*	Return database engine
	*	@return string
	**/
	function driver() {
		return $this->engine;
	}

	/**
	*	Return server version
	*	@return string
	**/
	function version() {
		return $this->pdo->getattribute(\PDO::ATTR_SERVER_VERSION);
	}

	/**
	*	Return database name
	*	@return string
	**/
	function name() {
		return $this->dbname;
	}

	/**
	*	Return quoted identifier name
	*	@return string
	*	@param $key
	*	@param bool $split
	 **/
	function quotekey($key, $split=TRUE) {
		$delims=[
			'sqlite2?|mysql'=>'``',
			'pgsql|oci'=>'""',
			'mssql|sqlsrv|odbc|sybase|dblib'=>'[]'
		];
		$use='';
		foreach ($delims as $engine=>$delim)
			if (preg_match('/'.$engine.'/',$this->engine)) {
				$use=$delim;
				break;
			}
		return $use[0].($split ? implode($use[1].'.'.$use[0],explode('.',$key))
			: $key).$use[1];
	}

	/**
	*	Redirect call to PDO object
	*	@return mixed
	*	@param $func string
	*	@param $args array
	**/
	function __call($func,array $args) {
		return call_user_func_array([$this->pdo,$func],$args);
	}

	//! Prohibit cloning
	private function __clone() {
	}

	/**
	*	Instantiate class
	*	@param $dsn string
	*	@param $user string
	*	@param $pw string
	*	@param $options array
	**/
	function __construct($dsn,$user=NULL,$pw=NULL,array $options=NULL) {
		$fw=\Base::instance();
		$this->uuid=$fw->hash($this->dsn=$dsn);
		if (preg_match('/^.+?(?:dbname|database)=(.+?)(?=;|$)/is',$dsn,$parts))
			$this->dbname=$parts[1];
		if (!$options)
			$options=[];
		if (isset($parts[0]) && strstr($parts[0],':',TRUE)=='mysql')
			$options+=[\PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES '.
				strtolower(str_replace('-','',$fw->ENCODING)).';'];
		$this->pdo=new \PDO($dsn,$user,$pw,$options);
		$this->engine=$this->pdo->getattribute(\PDO::ATTR_DRIVER_NAME);
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/sql/mapper.php b/vendor/fatfree-3.6/lib/db/sql/mapper.php
new file mode 100755
index 0000000..afdc5c8
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/sql/mapper.php
@@ -0,0 +1,672 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB\SQL;

//! SQL data mapper
class Mapper extends \DB\Cursor {

	protected
		//! PDO wrapper
		$db,
		//! Database engine
		$engine,
		//! SQL table
		$source,
		//! SQL table (quoted)
		$table,
		//! Last insert ID
		$_id,
		//! Defined fields
		$fields,
		//! Adhoc fields
		$adhoc=[],
		//! Dynamic properties
		$props=[];

	/**
	*	Return database type
	*	@return string
	**/
	function dbtype() {
		return 'SQL';
	}

	/**
	*	Return mapped table
	*	@return string
	**/
	function table() {
		return $this->source;
	}

	/**
	*	Return TRUE if any/specified field value has changed
	*	@return bool
	*	@param $key string
	**/
	function changed($key=NULL) {
		if (isset($key))
			return $this->fields[$key]['changed'];
		foreach($this->fields as $key=>$field)
			if ($field['changed'])
				return TRUE;
		return FALSE;
	}

	/**
	*	Return TRUE if field is defined
	*	@return bool
	*	@param $key string
	**/
	function exists($key) {
		return array_key_exists($key,$this->fields+$this->adhoc);
	}

	/**
	*	Assign value to field
	*	@return scalar
	*	@param $key string
	*	@param $val scalar
	**/
	function set($key,$val) {
		if (array_key_exists($key,$this->fields)) {
			$val=is_null($val) && $this->fields[$key]['nullable']?
				NULL:$this->db->value($this->fields[$key]['pdo_type'],$val);
			if ($this->fields[$key]['initial']!==$val ||
				$this->fields[$key]['default']!==$val && is_null($val))
				$this->fields[$key]['changed']=TRUE;
			return $this->fields[$key]['value']=$val;
		}
		// Adjust result on existing expressions
		if (isset($this->adhoc[$key]))
			$this->adhoc[$key]['value']=$val;
		elseif (is_string($val))
			// Parenthesize expression in case it's a subquery
			$this->adhoc[$key]=['expr'=>'('.$val.')','value'=>NULL];
		else
			$this->props[$key]=$val;
		return $val;
	}

	/**
	*	Retrieve value of field
	*	@return scalar
	*	@param $key string
	**/
	function &get($key) {
		if ($key=='_id')
			return $this->_id;
		elseif (array_key_exists($key,$this->fields))
			return $this->fields[$key]['value'];
		elseif (array_key_exists($key,$this->adhoc))
			return $this->adhoc[$key]['value'];
		elseif (array_key_exists($key,$this->props))
			return $this->props[$key];
		user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
	}

	/**
	*	Clear value of field
	*	@return NULL
	*	@param $key string
	**/
	function clear($key) {
		if (array_key_exists($key,$this->adhoc))
			unset($this->adhoc[$key]);
		else
			unset($this->props[$key]);
	}

	/**
	*	Invoke dynamic method
	*	@return mixed
	*	@param $func string
	*	@param $args array
	**/
	function __call($func,$args) {
		return call_user_func_array(
			(array_key_exists($func,$this->props)?
				$this->props[$func]:
				$this->$func),$args
		);
	}

	/**
	*	Convert array to mapper object
	*	@return static
	*	@param $row array
	**/
	protected function factory($row) {
		$mapper=clone($this);
		$mapper->reset();
		foreach ($row as $key=>$val) {
			if (array_key_exists($key,$this->fields))
				$var='fields';
			elseif (array_key_exists($key,$this->adhoc))
				$var='adhoc';
			else
				continue;
			$mapper->{$var}[$key]['value']=$val;
			$mapper->{$var}[$key]['initial']=$val;
			if ($var=='fields' && $mapper->{$var}[$key]['pkey'])
				$mapper->{$var}[$key]['previous']=$val;
		}
		$mapper->query=[clone($mapper)];
		if (isset($mapper->trigger['load']))
			\Base::instance()->call($mapper->trigger['load'],$mapper);
		return $mapper;
	}

	/**
	*	Return fields of mapper object as an associative array
	*	@return array
	*	@param $obj object
	**/
	function cast($obj=NULL) {
		if (!$obj)
			$obj=$this;
		return array_map(
			function($row) {
				return $row['value'];
			},
			$obj->fields+$obj->adhoc
		);
	}

	/**
	*	Build query string and arguments
	*	@return array
	*	@param $fields string
	*	@param $filter string|array
	*	@param $options array
	**/
	function stringify($fields,$filter=NULL,array $options=NULL) {
		if (!$options)
			$options=[];
		$options+=[
			'group'=>NULL,
			'order'=>NULL,
			'limit'=>0,
			'offset'=>0
		];
		$db=$this->db;
		$sql='SELECT '.$fields.' FROM '.$this->table;
		$args=[];
		if (is_array($filter)) {
			$args=isset($filter[1]) && is_array($filter[1])?
				$filter[1]:
				array_slice($filter,1,NULL,TRUE);
			$args=is_array($args)?$args:[1=>$args];
			list($filter)=$filter;
		}
		if ($filter)
			$sql.=' WHERE '.$filter;
		if ($options['group']) {
			$sql.=' GROUP BY '.implode(',',array_map(
				function($str) use($db) {
					return preg_replace_callback(
						'/\b(\w+[._\-\w]*)\h*(HAVING.+|$)/i',
						function($parts) use($db) {
							return $db->quotekey($parts[1]).
								(isset($parts[2])?(' '.$parts[2]):'');
						},
						$str
					);
				},
				explode(',',$options['group'])));
		}
		if ($options['order']) {
			$char=substr($db->quotekey(''),0,1);// quoting char
			$order=' ORDER BY '.(FALSE===strpos($options['order'],$char)?
				implode(',',array_map(function($str) use($db) {
					return preg_match('/^\h*(\w+[._\-\w]*)(?:\h+((?:ASC|DESC)[\w\h]*))?\h*$/i',
						$str,$parts)?
						($db->quotekey($parts[1]).
						(isset($parts[2])?(' '.$parts[2]):'')):$str;
				},explode(',',$options['order']))):
				$options['order']);
		}
		// SQL Server fixes
		if (preg_match('/mssql|sqlsrv|odbc/', $this->engine) &&
			($options['limit'] || $options['offset'])) {
			// order by pkey when no ordering option was given
			if (!$options['order'])
				foreach ($this->fields as $key=>$field)
					if ($field['pkey']) {
						$order=' ORDER BY '.$db->quotekey($key);
						break;
					}
			$ofs=$options['offset']?(int)$options['offset']:0;
			$lmt=$options['limit']?(int)$options['limit']:0;
			if (strncmp($db->version(),'11',2)>=0) {
				// SQL Server >= 2012
				$sql.=$order.' OFFSET '.$ofs.' ROWS';
				if ($lmt)
					$sql.=' FETCH NEXT '.$lmt.' ROWS ONLY';
			}
			else {
				// SQL Server 2008
				$sql=preg_replace('/SELECT/',
					'SELECT '.
					($lmt>0?'TOP '.($ofs+$lmt):'').' ROW_NUMBER() '.
					'OVER ('.$order.') AS rnum,',$sql.$order,1);
				$sql='SELECT * FROM ('.$sql.') x WHERE rnum > '.($ofs);
			}
		}
		else {
			if (isset($order))
				$sql.=$order;
			if ($options['limit'])
				$sql.=' LIMIT '.(int)$options['limit'];
			if ($options['offset'])
				$sql.=' OFFSET '.(int)$options['offset'];
		}
		return [$sql,$args];
	}

	/**
	*	Build query string and execute
	*	@return static[]
	*	@param $fields string
	*	@param $filter string|array
	*	@param $options array
	*	@param $ttl int|array
	**/
	function select($fields,$filter=NULL,array $options=NULL,$ttl=0) {
		list($sql,$args)=$this->stringify($fields,$filter,$options);
		$result=$this->db->exec($sql,$args,$ttl);
		$out=[];
		foreach ($result as &$row) {
			foreach ($row as $field=>&$val) {
				if (array_key_exists($field,$this->fields)) {
					if (!is_null($val) || !$this->fields[$field]['nullable'])
						$val=$this->db->value(
							$this->fields[$field]['pdo_type'],$val);
				}
				unset($val);
			}
			$out[]=$this->factory($row);
			unset($row);
		}
		return $out;
	}

	/**
	*	Return records that match criteria
	*	@return static[]
	*	@param $filter string|array
	*	@param $options array
	*	@param $ttl int|array
	**/
	function find($filter=NULL,array $options=NULL,$ttl=0) {
		if (!$options)
			$options=[];
		$options+=[
			'group'=>NULL,
			'order'=>NULL,
			'limit'=>0,
			'offset'=>0
		];
		$adhoc='';
		foreach ($this->adhoc as $key=>$field)
			$adhoc.=','.$field['expr'].' AS '.$this->db->quotekey($key);
		return $this->select(
			($options['group'] && !preg_match('/mysql|sqlite/',$this->engine)?
				$options['group']:
				implode(',',array_map([$this->db,'quotekey'],
					array_keys($this->fields)))).$adhoc,$filter,$options,$ttl);
	}

	/**
	*	Count records that match criteria
	*	@return int
	*	@param $filter string|array
	*	@param $options array
	*	@param $ttl int|array
	**/
	function count($filter=NULL,array $options=NULL,$ttl=0) {
		$adhoc='';
		foreach ($this->adhoc as $key=>$field)
			$adhoc.=','.$field['expr'].' AS '.$this->db->quotekey($key);
		$fields='*'.$adhoc;
		if (preg_match('/mssql|dblib|sqlsrv/',$this->engine))
			$fields='TOP 100 PERCENT '.$fields;
		list($sql,$args)=$this->stringify($fields,$filter,$options);
		$sql='SELECT COUNT(*) AS '.$this->db->quotekey('_rows').' '.
			'FROM ('.$sql.') AS '.$this->db->quotekey('_temp');
		$result=$this->db->exec($sql,$args,$ttl);
		return (int)$result[0]['_rows'];
	}

	/**
	*	Return record at specified offset using same criteria as
	*	previous load() call and make it active
	*	@return static
	*	@param $ofs int
	**/
	function skip($ofs=1) {
		$out=parent::skip($ofs);
		$dry=$this->dry();
		foreach ($this->fields as $key=>&$field) {
			$field['value']=$dry?NULL:$out->fields[$key]['value'];
			$field['initial']=$field['value'];
			$field['changed']=FALSE;
			if ($field['pkey'])
				$field['previous']=$dry?NULL:$out->fields[$key]['value'];
			unset($field);
		}
		foreach ($this->adhoc as $key=>&$field) {
			$field['value']=$dry?NULL:$out->adhoc[$key]['value'];
			unset($field);
		}
		if (!$dry && isset($this->trigger['load']))
			\Base::instance()->call($this->trigger['load'],$this);
		return $out;
	}

	/**
	*	Insert new record
	*	@return static
	**/
	function insert() {
		$args=[];
		$actr=0;
		$nctr=0;
		$fields='';
		$values='';
		$filter='';
		$pkeys=[];
		$nkeys=[];
		$ckeys=[];
		$inc=NULL;
		foreach ($this->fields as $key=>$field)
			if ($field['pkey'])
				$pkeys[$key]=$field['previous'];
		if (isset($this->trigger['beforeinsert']) &&
			\Base::instance()->call($this->trigger['beforeinsert'],
				[$this,$pkeys])===FALSE)
			return $this;
		foreach ($this->fields as $key=>&$field) {
			if ($field['pkey']) {
				$field['previous']=$field['value'];
				if (!$inc && $field['pdo_type']==\PDO::PARAM_INT &&
					empty($field['value']) && !$field['nullable'])
					$inc=$key;
				$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
				$nkeys[$nctr+1]=[$field['value'],$field['pdo_type']];
				$nctr++;
			}
			if ($field['changed'] && $key!=$inc) {
				$fields.=($actr?',':'').$this->db->quotekey($key);
				$values.=($actr?',':'').'?';
				$args[$actr+1]=[$field['value'],$field['pdo_type']];
				$actr++;
				$ckeys[]=$key;
			}
		}
		if ($fields) {
			$add=$aik='';
			if ($this->engine=='pgsql' && !empty($pkeys)) {
				$names=array_keys($pkeys);
				$aik=end($names);
				$add=' RETURNING '.$this->db->quotekey($aik);
			}
			$lID=$this->db->exec(
				(preg_match('/mssql|dblib|sqlsrv/',$this->engine) &&
				array_intersect(array_keys($pkeys),$ckeys)?
					'SET IDENTITY_INSERT '.$this->table.' ON;':'').
				'INSERT INTO '.$this->table.' ('.$fields.') '.
				'VALUES ('.$values.')'.$add,$args
			);
			if ($this->engine=='pgsql' && $lID && $aik)
				$this->_id=$lID[0][$aik];
			elseif ($this->engine!='oci')
				$this->_id=$this->db->lastinsertid();
			// Reload to obtain default and auto-increment field values
			if ($reload=(($inc && $this->_id) || $filter))
				$this->load($inc?
					[$inc.'=?',$this->db->value(
						$this->fields[$inc]['pdo_type'],$this->_id)]:
					[$filter,$nkeys]);
			if (isset($this->trigger['afterinsert']))
				\Base::instance()->call($this->trigger['afterinsert'],
					[$this,$pkeys]);
			// reset changed flag after calling afterinsert
			if (!$reload)
				foreach ($this->fields as $key=>&$field) {
					$field['changed']=FALSE;
					$field['initial']=$field['value'];
					unset($field);
				}
		}
		return $this;
	}

	/**
	*	Update current record
	*	@return static
	**/
	function update() {
		$args=[];
		$ctr=0;
		$pairs='';
		$filter='';
		$pkeys=[];
		foreach ($this->fields as $key=>$field)
			if ($field['pkey'])
				$pkeys[$key]=$field['previous'];
		if (isset($this->trigger['beforeupdate']) &&
			\Base::instance()->call($this->trigger['beforeupdate'],
				[$this,$pkeys])===FALSE)
			return $this;
		foreach ($this->fields as $key=>$field)
			if ($field['changed']) {
				$pairs.=($pairs?',':'').$this->db->quotekey($key).'=?';
				$args[++$ctr]=[$field['value'],$field['pdo_type']];
			}
		foreach ($this->fields as $key=>$field)
			if ($field['pkey']) {
				$filter.=($filter?' AND ':' WHERE ').
					$this->db->quotekey($key).'=?';
				$args[++$ctr]=[$field['previous'],$field['pdo_type']];
			}
		if ($pairs) {
			$sql='UPDATE '.$this->table.' SET '.$pairs.$filter;
			$this->db->exec($sql,$args);
		}
		if (isset($this->trigger['afterupdate']))
			\Base::instance()->call($this->trigger['afterupdate'],
				[$this,$pkeys]);
		// reset changed flag after calling afterupdate
		foreach ($this->fields as $key=>&$field) {
				$field['changed']=FALSE;
				$field['initial']=$field['value'];
				unset($field);
			}
		return $this;
	}

	/**
	*	Delete current record
	*	@return int
	*	@param $quick bool
	*	@param $filter string|array
	**/
	function erase($filter=NULL,$quick=TRUE) {
		if (isset($filter)) {
			if (!$quick) {
				$out=0;
				foreach ($this->find($filter) as $mapper)
					$out+=$mapper->erase();
				return $out;
			}
			$args=[];
			if (is_array($filter)) {
				$args=isset($filter[1]) && is_array($filter[1])?
					$filter[1]:
					array_slice($filter,1,NULL,TRUE);
				$args=is_array($args)?$args:[1=>$args];
				list($filter)=$filter;
			}
			return $this->db->
				exec('DELETE FROM '.$this->table.
				($filter?' WHERE '.$filter:'').';',$args);
		}
		$args=[];
		$ctr=0;
		$filter='';
		$pkeys=[];
		foreach ($this->fields as $key=>&$field) {
			if ($field['pkey']) {
				$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
				$args[$ctr+1]=[$field['previous'],$field['pdo_type']];
				$pkeys[$key]=$field['previous'];
				$ctr++;
			}
			$field['value']=NULL;
			$field['changed']=(bool)$field['default'];
			if ($field['pkey'])
				$field['previous']=NULL;
			unset($field);
		}
		foreach ($this->adhoc as &$field) {
			$field['value']=NULL;
			unset($field);
		}
		parent::erase();
		if (isset($this->trigger['beforeerase']) &&
			\Base::instance()->call($this->trigger['beforeerase'],
				[$this,$pkeys])===FALSE)
			return 0;
		$out=$this->db->
			exec('DELETE FROM '.$this->table.' WHERE '.$filter.';',$args);
		if (isset($this->trigger['aftererase']))
			\Base::instance()->call($this->trigger['aftererase'],
				[$this,$pkeys]);
		return $out;
	}

	/**
	*	Reset cursor
	*	@return NULL
	**/
	function reset() {
		foreach ($this->fields as &$field) {
			$field['value']=NULL;
			$field['initial']=NULL;
			$field['changed']=FALSE;
			if ($field['pkey'])
				$field['previous']=NULL;
			unset($field);
		}
		foreach ($this->adhoc as &$field) {
			$field['value']=NULL;
			unset($field);
		}
		parent::reset();
	}

	/**
	*	Hydrate mapper object using hive array variable
	*	@return NULL
	*	@param $var array|string
	*	@param $func callback
	**/
	function copyfrom($var,$func=NULL) {
		if (is_string($var))
			$var=\Base::instance()->$var;
		if ($func)
			$var=call_user_func($func,$var);
		foreach ($var as $key=>$val)
			if (in_array($key,array_keys($this->fields)))
				$this->set($key,$val);
	}

	/**
	*	Populate hive array variable with mapper fields
	*	@return NULL
	*	@param $key string
	**/
	function copyto($key) {
		$var=&\Base::instance()->ref($key);
		foreach ($this->fields+$this->adhoc as $key=>$field)
			$var[$key]=$field['value'];
	}

	/**
	*	Return schema and, if the first argument is provided, update it
	*	@return array
	*	@param $fields NULL|array
	**/
	function schema($fields=null) {
		if ($fields)
			$this->fields = $fields;
		return $this->fields;
	}

	/**
	*	Return field names
	*	@return array
	*	@param $adhoc bool
	**/
	function fields($adhoc=TRUE) {
		return array_keys($this->fields+($adhoc?$this->adhoc:[]));
	}

	/**
	*	Return TRUE if field is not nullable
	*	@return bool
	*	@param $field string
	**/
	function required($field) {
		return isset($this->fields[$field]) &&
			!$this->fields[$field]['nullable'];
	}

	/**
	*	Retrieve external iterator for fields
	*	@return object
	**/
	function getiterator() {
		return new \ArrayIterator($this->cast());
	}

	/**
	*	Instantiate class
	*	@param $db \DB\SQL
	*	@param $table string
	*	@param $fields array|string
	*	@param $ttl int|array
	**/
	function __construct(\DB\SQL $db,$table,$fields=NULL,$ttl=60) {
		$this->db=$db;
		$this->engine=$db->driver();
		if ($this->engine=='oci')
			$table=strtoupper($table);
		$this->source=$table;
		$this->table=$this->db->quotekey($table);
		$this->fields=$db->schema($table,$fields,$ttl);
		$this->reset();
	}

}
diff --git a/vendor/fatfree-3.6/lib/db/sql/session.php b/vendor/fatfree-3.6/lib/db/sql/session.php
new file mode 100755
index 0000000..799cd0f
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/db/sql/session.php
@@ -0,0 +1,217 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace DB\SQL;

//! SQL-managed session handler
class Session extends Mapper {

	protected
		//! Session ID
		$sid,
		//! Anti-CSRF token
		$_csrf,
		//! User agent
		$_agent,
		//! IP,
		$_ip,
		//! Suspect callback
		$onsuspect;

	/**
	*	Open session
	*	@return TRUE
	*	@param $path string
	*	@param $name string
	**/
	function open($path,$name) {
		return TRUE;
	}

	/**
	*	Close session
	*	@return TRUE
	**/
	function close() {
		$this->reset();
		$this->sid=NULL;
		return TRUE;
	}

	/**
	*	Return session data in serialized format
	*	@return string
	*	@param $id string
	**/
	function read($id) {
		$this->load(['session_id=?',$this->sid=$id]);
		if ($this->dry())
			return '';
		if ($this->get('ip')!=$this->_ip || $this->get('agent')!=$this->_agent) {
			$fw=\Base::instance();
			if (!isset($this->onsuspect) ||
				$fw->call($this->onsuspect,[$this,$id])===FALSE) {
				//NB: `session_destroy` can't be called at that stage (`session_start` not completed)
				$this->destroy($id);
				$this->close();
				unset($fw->{'COOKIE.'.session_name()});
				$fw->error(403);
			}
		}
		return $this->get('data');
	}

	/**
	*	Write session data
	*	@return TRUE
	*	@param $id string
	*	@param $data string
	**/
	function write($id,$data) {
		$this->set('session_id',$id);
		$this->set('data',$data);
		$this->set('ip',$this->_ip);
		$this->set('agent',$this->_agent);
		$this->set('stamp',time());
		$this->save();
		return TRUE;
	}

	/**
	*	Destroy session
	*	@return TRUE
	*	@param $id string
	**/
	function destroy($id) {
		$this->erase(['session_id=?',$id]);
		return TRUE;
	}

	/**
	*	Garbage collector
	*	@return TRUE
	*	@param $max int
	**/
	function cleanup($max) {
		$this->erase(['stamp+?<?',$max,time()]);
		return TRUE;
	}

	/**
	*	Return session id (if session has started)
	*	@return string|NULL
	**/
	function sid() {
		return $this->sid;
	}

	/**
	*	Return anti-CSRF token
	*	@return string
	**/
	function csrf() {
		return $this->_csrf;
	}

	/**
	*	Return IP address
	*	@return string
	**/
	function ip() {
		return $this->_ip;
	}

	/**
	*	Return Unix timestamp
	*	@return string|FALSE
	**/
	function stamp() {
		if (!$this->sid)
			session_start();
		return $this->dry()?FALSE:$this->get('stamp');
	}

	/**
	*	Return HTTP user agent
	*	@return string
	**/
	function agent() {
		return $this->_agent;
	}

	/**
	*	Instantiate class
	*	@param $db \DB\SQL
	*	@param $table string
	*	@param $force bool
	*	@param $onsuspect callback
	*	@param $key string
	**/
	function __construct(\DB\SQL $db,$table='sessions',$force=TRUE,$onsuspect=NULL,$key=NULL) {
		if ($force) {
			$eol="\n";
			$tab="\t";
			$sqlsrv=preg_match('/mssql|sqlsrv|sybase/',$db->driver());
			$db->exec(
				($sqlsrv?
					('IF NOT EXISTS (SELECT * FROM sysobjects WHERE '.
						'name='.$db->quote($table).' AND xtype=\'U\') '.
						'CREATE TABLE dbo.'):
					('CREATE TABLE IF NOT EXISTS '.
						((($name=$db->name())&&$db->driver()!='pgsql')?
							($db->quotekey($name,FALSE).'.'):''))).
				$db->quotekey($table,FALSE).' ('.$eol.
					($sqlsrv?$tab.$db->quotekey('id').' INT IDENTITY,'.$eol:'').
					$tab.$db->quotekey('session_id').' VARCHAR(255),'.$eol.
					$tab.$db->quotekey('data').' TEXT,'.$eol.
					$tab.$db->quotekey('ip').' VARCHAR(45),'.$eol.
					$tab.$db->quotekey('agent').' VARCHAR(300),'.$eol.
					$tab.$db->quotekey('stamp').' INTEGER,'.$eol.
					$tab.'PRIMARY KEY ('.$db->quotekey($sqlsrv?'id':'session_id').')'.$eol.
				($sqlsrv?',CONSTRAINT [UK_session_id] UNIQUE(session_id)':'').
				');'
			);
		}
		parent::__construct($db,$table);
		$this->onsuspect=$onsuspect;
		session_set_save_handler(
			[$this,'open'],
			[$this,'close'],
			[$this,'read'],
			[$this,'write'],
			[$this,'destroy'],
			[$this,'cleanup']
		);
		register_shutdown_function('session_commit');
		$fw=\Base::instance();
		$headers=$fw->HEADERS;
		$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
		if ($key)
			$fw->$key=$this->_csrf;
		$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
		if (strlen($this->_agent) > 300) {
			$this->_agent = substr($this->_agent, 0, 300);
		}
		$this->_ip=$fw->IP;
	}

}
diff --git a/vendor/fatfree-3.6/lib/f3.php b/vendor/fatfree-3.6/lib/f3.php
new file mode 100755
index 0000000..3b96a63
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/f3.php
@@ -0,0 +1,42 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Legacy mode enabler
class F3 {

	static
		//! Framework instance
		$fw;

	/**
	*	Forward function calls to framework
	*	@return mixed
	*	@param $func callback
	*	@param $args array
	**/
	static function __callstatic($func,array $args) {
		if (!self::$fw)
			self::$fw=Base::instance();
		return call_user_func_array([self::$fw,$func],$args);
	}

}
diff --git a/vendor/fatfree-3.6/lib/image.php b/vendor/fatfree-3.6/lib/image.php
new file mode 100755
index 0000000..3efc0cc
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/image.php
@@ -0,0 +1,616 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Image manipulation tools
class Image {

	//@{ Messages
	const
		E_Color='Invalid color specified: %s',
		E_File='File not found',
		E_Font='CAPTCHA font not found',
		E_TTF='No TrueType support in GD module',
		E_Length='Invalid CAPTCHA length: %s';
	//@}

	//@{ Positional cues
	const
		POS_Left=1,
		POS_Center=2,
		POS_Right=4,
		POS_Top=8,
		POS_Middle=16,
		POS_Bottom=32;
	//@}

	protected
		//! Source filename
		$file,
		//! Image resource
		$data,
		//! Enable/disable history
		$flag=FALSE,
		//! Filter count
		$count=0;

	/**
	*	Convert RGB hex triad to array
	*	@return array|FALSE
	*	@param $color int|string
	**/
	function rgb($color) {
		if (is_string($color))
			$color=hexdec($color);
		$hex=str_pad($hex=dechex($color),$color<4096?3:6,'0',STR_PAD_LEFT);
		if (($len=strlen($hex))>6)
			user_error(sprintf(self::E_Color,'0x'.$hex),E_USER_ERROR);
		$color=str_split($hex,$len/3);
		foreach ($color as &$hue) {
			$hue=hexdec(str_repeat($hue,6/$len));
			unset($hue);
		}
		return $color;
	}

	/**
	*	Invert image
	*	@return object
	**/
	function invert() {
		imagefilter($this->data,IMG_FILTER_NEGATE);
		return $this->save();
	}

	/**
	*	Adjust brightness (range:-255 to 255)
	*	@return object
	*	@param $level int
	**/
	function brightness($level) {
		imagefilter($this->data,IMG_FILTER_BRIGHTNESS,$level);
		return $this->save();
	}

	/**
	*	Adjust contrast (range:-100 to 100)
	*	@return object
	*	@param $level int
	**/
	function contrast($level) {
		imagefilter($this->data,IMG_FILTER_CONTRAST,$level);
		return $this->save();
	}

	/**
	*	Convert to grayscale
	*	@return object
	**/
	function grayscale() {
		imagefilter($this->data,IMG_FILTER_GRAYSCALE);
		return $this->save();
	}

	/**
	*	Adjust smoothness
	*	@return object
	*	@param $level int
	**/
	function smooth($level) {
		imagefilter($this->data,IMG_FILTER_SMOOTH,$level);
		return $this->save();
	}

	/**
	*	Emboss the image
	*	@return object
	**/
	function emboss() {
		imagefilter($this->data,IMG_FILTER_EMBOSS);
		return $this->save();
	}

	/**
	*	Apply sepia effect
	*	@return object
	**/
	function sepia() {
		imagefilter($this->data,IMG_FILTER_GRAYSCALE);
		imagefilter($this->data,IMG_FILTER_COLORIZE,90,60,45);
		return $this->save();
	}

	/**
	*	Pixelate the image
	*	@return object
	*	@param $size int
	**/
	function pixelate($size) {
		imagefilter($this->data,IMG_FILTER_PIXELATE,$size,TRUE);
		return $this->save();
	}

	/**
	*	Blur the image using Gaussian filter
	*	@return object
	*	@param $selective bool
	**/
	function blur($selective=FALSE) {
		imagefilter($this->data,
			$selective?IMG_FILTER_SELECTIVE_BLUR:IMG_FILTER_GAUSSIAN_BLUR);
		return $this->save();
	}

	/**
	*	Apply sketch effect
	*	@return object
	**/
	function sketch() {
		imagefilter($this->data,IMG_FILTER_MEAN_REMOVAL);
		return $this->save();
	}

	/**
	*	Flip on horizontal axis
	*	@return object
	**/
	function hflip() {
		$tmp=imagecreatetruecolor(
			$width=$this->width(),$height=$this->height());
		imagesavealpha($tmp,TRUE);
		imagefill($tmp,0,0,IMG_COLOR_TRANSPARENT);
		imagecopyresampled($tmp,$this->data,
			0,0,$width-1,0,$width,$height,-$width,$height);
		imagedestroy($this->data);
		$this->data=$tmp;
		return $this->save();
	}

	/**
	*	Flip on vertical axis
	*	@return object
	**/
	function vflip() {
		$tmp=imagecreatetruecolor(
			$width=$this->width(),$height=$this->height());
		imagesavealpha($tmp,TRUE);
		imagefill($tmp,0,0,IMG_COLOR_TRANSPARENT);
		imagecopyresampled($tmp,$this->data,
			0,0,0,$height-1,$width,$height,$width,-$height);
		imagedestroy($this->data);
		$this->data=$tmp;
		return $this->save();
	}

	/**
	*	Crop the image
	*	@return object
	*	@param $x1 int
	*	@param $y1 int
	*	@param $x2 int
	*	@param $y2 int
	**/
	function crop($x1,$y1,$x2,$y2) {
		$tmp=imagecreatetruecolor($width=$x2-$x1+1,$height=$y2-$y1+1);
		imagesavealpha($tmp,TRUE);
		imagefill($tmp,0,0,IMG_COLOR_TRANSPARENT);
		imagecopyresampled($tmp,$this->data,
			0,0,$x1,$y1,$width,$height,$width,$height);
		imagedestroy($this->data);
		$this->data=$tmp;
		return $this->save();
	}

	/**
	*	Resize image (Maintain aspect ratio); Crop relative to center
	*	if flag is enabled; Enlargement allowed if flag is enabled
	*	@return object
	*	@param $width int
	*	@param $height int
	*	@param $crop bool
	*	@param $enlarge bool
	**/
	function resize($width=NULL,$height=NULL,$crop=TRUE,$enlarge=TRUE) {
		if (is_null($width) && is_null($height))
			return $this;
		$origw=$this->width();
		$origh=$this->height();
		if (is_null($width))
			$width=round(($height/$origh)*$origw);
		if (is_null($height))
			$height=round(($width/$origw)*$origh);
		// Adjust dimensions; retain aspect ratio
		$ratio=$origw/$origh;
		if (!$crop) {
			if ($width/$ratio<=$height)
				$height=round($width/$ratio);
			else
				$width=round($height*$ratio);
		}
		if (!$enlarge) {
			$width=min($origw,$width);
			$height=min($origh,$height);
		}
		// Create blank image
		$tmp=imagecreatetruecolor($width,$height);
		imagesavealpha($tmp,TRUE);
		imagefill($tmp,0,0,IMG_COLOR_TRANSPARENT);
		// Resize
		if ($crop) {
			if ($width/$ratio<=$height) {
				$cropw=round($origh*$width/$height);
				imagecopyresampled($tmp,$this->data,
					0,0,($origw-$cropw)/2,0,$width,$height,$cropw,$origh);
			}
			else {
				$croph=round($origw*$height/$width);
				imagecopyresampled($tmp,$this->data,
					0,0,0,($origh-$croph)/2,$width,$height,$origw,$croph);
			}
		}
		else
			imagecopyresampled($tmp,$this->data,
				0,0,0,0,$width,$height,$origw,$origh);
		imagedestroy($this->data);
		$this->data=$tmp;
		return $this->save();
	}

	/**
	*	Rotate image
	*	@return object
	*	@param $angle int
	**/
	function rotate($angle) {
		$this->data=imagerotate($this->data,$angle,
			imagecolorallocatealpha($this->data,0,0,0,127));
		imagesavealpha($this->data,TRUE);
		return $this->save();
	}

	/**
	*	Apply an image overlay
	*	@return object
	*	@param $img object
	*	@param $align int|array
	*	@param $alpha int
	**/
	function overlay(Image $img,$align=NULL,$alpha=100) {
		if (is_null($align))
			$align=self::POS_Right|self::POS_Bottom;
		if (is_array($align)) {
			list($posx,$posy)=$align;
			$align = 0;
		}
		$ovr=imagecreatefromstring($img->dump());
		imagesavealpha($ovr,TRUE);
		$imgw=$this->width();
		$imgh=$this->height();
		$ovrw=imagesx($ovr);
		$ovrh=imagesy($ovr);
		if ($align & self::POS_Left)
			$posx=0;
		if ($align & self::POS_Center)
			$posx=($imgw-$ovrw)/2;
		if ($align & self::POS_Right)
			$posx=$imgw-$ovrw;
		if ($align & self::POS_Top)
			$posy=0;
		if ($align & self::POS_Middle)
			$posy=($imgh-$ovrh)/2;
		if ($align & self::POS_Bottom)
			$posy=$imgh-$ovrh;
		if (empty($posx))
			$posx=0;
		if (empty($posy))
			$posy=0;
		if ($alpha==100)
			imagecopy($this->data,$ovr,$posx,$posy,0,0,$ovrw,$ovrh);
		else {
			$cut=imagecreatetruecolor($ovrw,$ovrh);
			imagecopy($cut,$this->data,0,0,$posx,$posy,$ovrw,$ovrh);
			imagecopy($cut,$ovr,0,0,0,0,$ovrw,$ovrh);
			imagecopymerge($this->data,
				$cut,$posx,$posy,0,0,$ovrw,$ovrh,$alpha);
		}
		return $this->save();
	}

	/**
	*	Generate identicon
	*	@return object
	*	@param $str string
	*	@param $size int
	*	@param $blocks int
	**/
	function identicon($str,$size=64,$blocks=4) {
		$sprites=[
			[.5,1,1,0,1,1],
			[.5,0,1,0,.5,1,0,1],
			[.5,0,1,0,1,1,.5,1,1,.5],
			[0,.5,.5,0,1,.5,.5,1,.5,.5],
			[0,.5,1,0,1,1,0,1,1,.5],
			[1,0,1,1,.5,1,1,.5,.5,.5],
			[0,0,1,0,1,.5,0,0,.5,1,0,1],
			[0,0,.5,0,1,.5,.5,1,0,1,.5,.5],
			[.5,0,.5,.5,1,.5,1,1,.5,1,.5,.5,0,.5],
			[0,0,1,0,.5,.5,1,.5,.5,1,.5,.5,0,1],
			[0,.5,.5,1,1,.5,.5,0,1,0,1,1,0,1],
			[.5,0,1,0,1,1,.5,1,1,.75,.5,.5,1,.25],
			[0,.5,.5,0,.5,.5,1,0,1,.5,.5,1,.5,.5,0,1],
			[0,0,1,0,1,1,0,1,1,.5,.5,.25,.5,.75,0,.5,.5,.25],
			[0,.5,.5,.5,.5,0,1,0,.5,.5,1,.5,.5,1,.5,.5,0,1],
			[0,0,1,0,.5,.5,.5,0,0,.5,1,.5,.5,1,.5,.5,0,1]
		];
		$hash=sha1($str);
		$this->data=imagecreatetruecolor($size,$size);
		list($r,$g,$b)=$this->rgb(hexdec(substr($hash,-3)));
		$fg=imagecolorallocate($this->data,$r,$g,$b);
		imagefill($this->data,0,0,IMG_COLOR_TRANSPARENT);
		$ctr=count($sprites);
		$dim=$blocks*floor($size/$blocks)*2/$blocks;
		for ($j=0,$y=ceil($blocks/2);$j<$y;$j++)
			for ($i=$j,$x=$blocks-1-$j;$i<$x;$i++) {
				$sprite=imagecreatetruecolor($dim,$dim);
				imagefill($sprite,0,0,IMG_COLOR_TRANSPARENT);
				$block=$sprites[hexdec($hash[($j*$blocks+$i)*2])%$ctr];
				for ($k=0,$pts=count($block);$k<$pts;$k++)
					$block[$k]*=$dim;
				imagefilledpolygon($sprite,$block,$pts/2,$fg);
				for ($k=0;$k<4;$k++) {
					imagecopyresampled($this->data,$sprite,
						$i*$dim/2,$j*$dim/2,0,0,$dim/2,$dim/2,$dim,$dim);
					$this->data=imagerotate($this->data,90,
						imagecolorallocatealpha($this->data,0,0,0,127));
				}
				imagedestroy($sprite);
			}
		imagesavealpha($this->data,TRUE);
		return $this->save();
	}

	/**
	*	Generate CAPTCHA image
	*	@return object|FALSE
	*	@param $font string
	*	@param $size int
	*	@param $len int
	*	@param $key string
	*	@param $path string
	*	@param $fg int
	*	@param $bg int
	**/
	function captcha($font,$size=24,$len=5,
		$key=NULL,$path='',$fg=0xFFFFFF,$bg=0x000000) {
		if ((!$ssl=extension_loaded('openssl')) && ($len<4 || $len>13)) {
			user_error(sprintf(self::E_Length,$len),E_USER_ERROR);
			return FALSE;
		}
		if (!function_exists('imagettftext')) {
			user_error(self::E_TTF,E_USER_ERROR);
			return FALSE;
		}
		$fw=Base::instance();
		foreach ($fw->split($path?:$fw->UI.';./') as $dir)
			if (is_file($path=$dir.$font)) {
				$seed=strtoupper(substr(
					$ssl?bin2hex(openssl_random_pseudo_bytes($len)):uniqid(),
					-$len));
				$block=$size*3;
				$tmp=[];
				for ($i=0,$width=0,$height=0;$i<$len;$i++) {
					// Process at 2x magnification
					$box=imagettfbbox($size*2,0,$path,$seed[$i]);
					$w=$box[2]-$box[0];
					$h=$box[1]-$box[5];
					$char=imagecreatetruecolor($block,$block);
					imagefill($char,0,0,$bg);
					imagettftext($char,$size*2,0,
						($block-$w)/2,$block-($block-$h)/2,
						$fg,$path,$seed[$i]);
					$char=imagerotate($char,mt_rand(-30,30),
						imagecolorallocatealpha($char,0,0,0,127));
					// Reduce to normal size
					$tmp[$i]=imagecreatetruecolor(
						($w=imagesx($char))/2,($h=imagesy($char))/2);
					imagefill($tmp[$i],0,0,IMG_COLOR_TRANSPARENT);
					imagecopyresampled($tmp[$i],
						$char,0,0,0,0,$w/2,$h/2,$w,$h);
					imagedestroy($char);
					$width+=$i+1<$len?$block/2:$w/2;
					$height=max($height,$h/2);
				}
				$this->data=imagecreatetruecolor($width,$height);
				imagefill($this->data,0,0,IMG_COLOR_TRANSPARENT);
				for ($i=0;$i<$len;$i++) {
					imagecopy($this->data,$tmp[$i],
						$i*$block/2,($height-imagesy($tmp[$i]))/2,0,0,
						imagesx($tmp[$i]),imagesy($tmp[$i]));
					imagedestroy($tmp[$i]);
				}
				imagesavealpha($this->data,TRUE);
				if ($key)
					$fw->$key=$seed;
				return $this->save();
			}
		user_error(self::E_Font,E_USER_ERROR);
		return FALSE;
	}

	/**
	*	Return image width
	*	@return int
	**/
	function width() {
		return imagesx($this->data);
	}

	/**
	*	Return image height
	*	@return int
	**/
	function height() {
		return imagesy($this->data);
	}

	/**
	*	Send image to HTTP client
	*	@return NULL
	**/
	function render() {
		$args=func_get_args();
		$format=$args?array_shift($args):'png';
		if (PHP_SAPI!='cli') {
			header('Content-Type: image/'.$format);
			header('X-Powered-By: '.Base::instance()->PACKAGE);
		}
		call_user_func_array(
			'image'.$format,
			array_merge([$this->data,NULL],$args)
		);
	}

	/**
	*	Return image as a string
	*	@return string
	**/
	function dump() {
		$args=func_get_args();
		$format=$args?array_shift($args):'png';
		ob_start();
		call_user_func_array(
			'image'.$format,
			array_merge([$this->data,NULL],$args)
		);
		return ob_get_clean();
	}

	/**
	*	Return image resource
	*	@return resource
	**/
	function data() {
		return $this->data;
	}

	/**
	*	Save current state
	*	@return object
	**/
	function save() {
		$fw=Base::instance();
		if ($this->flag) {
			if (!is_dir($dir=$fw->TEMP))
				mkdir($dir,Base::MODE,TRUE);
			$this->count++;
			$fw->write($dir.'/'.$fw->SEED.'.'.
				$fw->hash($this->file).'-'.$this->count.'.png',
				$this->dump());
		}
		return $this;
	}

	/**
	*	Revert to specified state
	*	@return object
	*	@param $state int
	**/
	function restore($state=1) {
		$fw=Base::instance();
		if ($this->flag && is_file($file=($path=$fw->TEMP.
			$fw->SEED.'.'.$fw->hash($this->file).'-').$state.'.png')) {
			if (is_resource($this->data))
				imagedestroy($this->data);
			$this->data=imagecreatefromstring($fw->read($file));
			imagesavealpha($this->data,TRUE);
			foreach (glob($path.'*.png',GLOB_NOSORT) as $match)
				if (preg_match('/-(\d+)\.png/',$match,$parts) &&
					$parts[1]>$state)
					@unlink($match);
			$this->count=$state;
		}
		return $this;
	}

	/**
	*	Undo most recently applied filter
	*	@return object
	**/
	function undo() {
		if ($this->flag) {
			if ($this->count)
				$this->count--;
			return $this->restore($this->count);
		}
		return $this;
	}

	/**
	*	Load string
	*	@return object|FALSE
	*	@param $str string
	**/
	function load($str) {
		if (!$this->data=@imagecreatefromstring($str))
			return FALSE;
		imagesavealpha($this->data,TRUE);
		$this->save();
		return $this;
	}

	/**
	*	Instantiate image
	*	@param $file string
	*	@param $flag bool
	*	@param $path string
	**/
	function __construct($file=NULL,$flag=FALSE,$path=NULL) {
		$this->flag=$flag;
		if ($file) {
			$fw=Base::instance();
			// Create image from file
			$this->file=$file;
			if (!isset($path))
				$path=$fw->UI.';./';
			foreach ($fw->split($path,FALSE) as $dir)
				if (is_file($dir.$file))
					return $this->load($fw->read($dir.$file));
			user_error(self::E_File,E_USER_ERROR);
		}
	}

	/**
	*	Wrap-up
	*	@return NULL
	**/
	function __destruct() {
		if (is_resource($this->data)) {
			imagedestroy($this->data);
			$fw=Base::instance();
			$path=$fw->TEMP.$fw->SEED.'.'.$fw->hash($this->file);
			if ($glob=@glob($path.'*.png',GLOB_NOSORT))
				foreach ($glob as $match)
					if (preg_match('/-(\d+)\.png/',$match))
						@unlink($match);
		}
	}

}
diff --git a/vendor/fatfree-3.6/lib/log.php b/vendor/fatfree-3.6/lib/log.php
new file mode 100755
index 0000000..422baf2
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/log.php
@@ -0,0 +1,67 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Custom logger
class Log {

	protected
		//! File name
		$file;

	/**
	*	Write specified text to log file
	*	@return string
	*	@param $text string
	*	@param $format string
	**/
	function write($text,$format='r') {
		$fw=Base::instance();
		$fw->write(
			$this->file,
			date($format).
				(isset($_SERVER['REMOTE_ADDR'])?
					(' ['.$_SERVER['REMOTE_ADDR'].']'):'').' '.
			trim($text).PHP_EOL,
			TRUE
		);
	}

	/**
	*	Erase log
	*	@return NULL
	**/
	function erase() {
		@unlink($this->file);
	}

	/**
	*	Instantiate class
	*	@param $file string
	**/
	function __construct($file) {
		$fw=Base::instance();
		if (!is_dir($dir=$fw->LOGS))
			mkdir($dir,Base::MODE,TRUE);
		$this->file=$dir.$file;
	}

}
diff --git a/vendor/fatfree-3.6/lib/magic.php b/vendor/fatfree-3.6/lib/magic.php
new file mode 100755
index 0000000..2502bf1
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/magic.php
@@ -0,0 +1,139 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! PHP magic wrapper
abstract class Magic implements ArrayAccess {

	/**
	*	Return TRUE if key is not empty
	*	@return bool
	*	@param $key string
	**/
	abstract function exists($key);

	/**
	*	Bind value to key
	*	@return mixed
	*	@param $key string
	*	@param $val mixed
	**/
	abstract function set($key,$val);

	/**
	*	Retrieve contents of key
	*	@return mixed
	*	@param $key string
	**/
	abstract function &get($key);

	/**
	*	Unset key
	*	@return NULL
	*	@param $key string
	**/
	abstract function clear($key);

	/**
	*	Convenience method for checking property value
	*	@return mixed
	*	@param $key string
	**/
	function offsetexists($key) {
		return Base::instance()->visible($this,$key)?
			isset($this->$key):$this->exists($key);
	}

	/**
	*	Convenience method for assigning property value
	*	@return mixed
	*	@param $key string
	*	@param $val scalar
	**/
	function offsetset($key,$val) {
		return Base::instance()->visible($this,$key)?
			($this->$key=$val):$this->set($key,$val);
	}

	/**
	*	Convenience method for retrieving property value
	*	@return mixed
	*	@param $key string
	**/
	function &offsetget($key) {
		if (Base::instance()->visible($this,$key))
			$val=&$this->$key;
		else
			$val=&$this->get($key);
		return $val;
	}

	/**
	*	Convenience method for removing property value
	*	@return NULL
	*	@param $key string
	**/
	function offsetunset($key) {
		if (Base::instance()->visible($this,$key))
			unset($this->$key);
		else
			$this->clear($key);
	}

	/**
	*	Alias for offsetexists()
	*	@return mixed
	*	@param $key string
	**/
	function __isset($key) {
		return $this->offsetexists($key);
	}

	/**
	*	Alias for offsetset()
	*	@return mixed
	*	@param $key string
	*	@param $val scalar
	**/
	function __set($key,$val) {
		return $this->offsetset($key,$val);
	}

	/**
	*	Alias for offsetget()
	*	@return mixed
	*	@param $key string
	**/
	function &__get($key) {
		$val=&$this->offsetget($key);
		return $val;
	}

	/**
	*	Alias for offsetunset()
	*	@return NULL
	*	@param $key string
	**/
	function __unset($key) {
		$this->offsetunset($key);
	}

}
diff --git a/vendor/fatfree-3.6/lib/markdown.php b/vendor/fatfree-3.6/lib/markdown.php
new file mode 100755
index 0000000..9abcd18
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/markdown.php
@@ -0,0 +1,572 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Markdown-to-HTML converter
class Markdown extends Prefab {

	protected
		//! Parsing rules
		$blocks,
		//! Special characters
		$special;

	/**
	*	Process blockquote
	*	@return string
	*	@param $str string
	**/
	protected function _blockquote($str) {
		$str=preg_replace('/(?<=^|\n)\h?>\h?(.*?(?:\n+|$))/','\1',$str);
		return strlen($str)?
			('<blockquote>'.$this->build($str).'</blockquote>'."\n\n"):'';
	}

	/**
	*	Process whitespace-prefixed code block
	*	@return string
	*	@param $str string
	**/
	protected function _pre($str) {
		$str=preg_replace('/(?<=^|\n)(?: {4}|\t)(.+?(?:\n+|$))/','\1',
			$this->esc($str));
		return strlen($str)?
			('<pre><code>'.
				$this->esc($this->snip($str)).
			'</code></pre>'."\n\n"):
			'';
	}

	/**
	*	Process fenced code block
	*	@return string
	*	@param $hint string
	*	@param $str string
	**/
	protected function _fence($hint,$str) {
		$str=$this->snip($str);
		$fw=Base::instance();
		if ($fw->HIGHLIGHT) {
			switch (strtolower($hint)) {
				case 'php':
					$str=$fw->highlight($str);
					break;
				case 'apache':
					preg_match_all('/(?<=^|\n)(\h*)'.
						'(?:(<\/?)(\w+)((?:\h+[^>]+)*)(>)|'.
						'(?:(\w+)(\h.+?)))(\h*(?:\n+|$))/',
						$str,$matches,PREG_SET_ORDER);
					$out='';
					foreach ($matches as $match)
						$out.=$match[1].
							($match[3]?
								('<span class="section">'.
									$this->esc($match[2]).$match[3].
								'</span>'.
								($match[4]?
									('<span class="data">'.
										$this->esc($match[4]).
									'</span>'):
									'').
								'<span class="section">'.
									$this->esc($match[5]).
								'</span>'):
								('<span class="directive">'.
									$match[6].
								'</span>'.
								'<span class="data">'.
									$this->esc($match[7]).
								'</span>')).
							$match[8];
					$str='<code>'.$out.'</code>';
					break;
				case 'html':
					preg_match_all(
						'/(?:(?:<(\/?)(\w+)'.
						'((?:\h+(?:\w+\h*=\h*)?".+?"|[^>]+)*|'.
						'\h+.+?)(\h*\/?)>)|(.+?))/s',
						$str,$matches,PREG_SET_ORDER
					);
					$out='';
					foreach ($matches as $match) {
						if ($match[2]) {
							$out.='<span class="xml_tag">&lt;'.
								$match[1].$match[2].'</span>';
							if ($match[3]) {
								preg_match_all(
									'/(?:\h+(?:(?:(\w+)\h*=\h*)?'.
									'(".+?")|(.+)))/',
									$match[3],$parts,PREG_SET_ORDER
								);
								foreach ($parts as $part)
									$out.=' '.
										(empty($part[3])?
											((empty($part[1])?
												'':
												('<span class="xml_attr">'.
													$part[1].'</span>=')).
											'<span class="xml_data">'.
												$part[2].'</span>'):
											('<span class="xml_tag">'.
												$part[3].'</span>'));
							}
							$out.='<span class="xml_tag">'.
								$match[4].'&gt;</span>';
						}
						else
							$out.=$this->esc($match[5]);
					}
					$str='<code>'.$out.'</code>';
					break;
				case 'ini':
					preg_match_all(
						'/(?<=^|\n)(?:'.
						'(;[^\n]*)|(?:<\?php.+?\?>?)|'.
						'(?:\[(.+?)\])|'.
						'(.+?)\h*=\h*'.
						'((?:\\\\\h*\r?\n|.+?)*)'.
						')((?:\r?\n)+|$)/',
						$str,$matches,PREG_SET_ORDER
					);
					$out='';
					foreach ($matches as $match) {
						if ($match[1])
							$out.='<span class="comment">'.$match[1].
								'</span>';
						elseif ($match[2])
							$out.='<span class="ini_section">['.$match[2].']'.
								'</span>';
						elseif ($match[3])
							$out.='<span class="ini_key">'.$match[3].
								'</span>='.
								($match[4]?
									('<span class="ini_value">'.
										$match[4].'</span>'):'');
						else
							$out.=$match[0];
						if (isset($match[5]))
							$out.=$match[5];
					}
					$str='<code>'.$out.'</code>';
					break;
				default:
					$str='<code>'.$this->esc($str).'</code>';
					break;
			}
		}
		else
			$str='<code>'.$this->esc($str).'</code>';
		return '<pre>'.$str.'</pre>'."\n\n";
	}

	/**
	*	Process horizontal rule
	*	@return string
	**/
	protected function _hr() {
		return '<hr />'."\n\n";
	}

	/**
	*	Process atx-style heading
	*	@return string
	*	@param $type string
	*	@param $str string
	**/
	protected function _atx($type,$str) {
		$level=strlen($type);
		return '<h'.$level.' id="'.Web::instance()->slug($str).'">'.
			$this->scan($str).'</h'.$level.'>'."\n\n";
	}

	/**
	*	Process setext-style heading
	*	@return string
	*	@param $str string
	*	@param $type string
	**/
	protected function _setext($str,$type) {
		$level=strpos('=-',$type)+1;
		return '<h'.$level.' id="'.Web::instance()->slug($str).'">'.
			$this->scan($str).'</h'.$level.'>'."\n\n";
	}

	/**
	*	Process ordered/unordered list
	*	@return string
	*	@param $str string
	**/
	protected function _li($str) {
		// Initialize list parser
		$len=strlen($str);
		$ptr=0;
		$dst='';
		$first=TRUE;
		$tight=TRUE;
		$type='ul';
		// Main loop
		while ($ptr<$len) {
			if (preg_match('/^\h*[*-](?:\h?[*-]){2,}(?:\n+|$)/',
				substr($str,$ptr),$match)) {
				$ptr+=strlen($match[0]);
				// Embedded horizontal rule
				return (strlen($dst)?
					('<'.$type.'>'."\n".$dst.'</'.$type.'>'."\n\n"):'').
					'<hr />'."\n\n".$this->build(substr($str,$ptr));
			}
			elseif (preg_match('/(?<=^|\n)([*+-]|\d+\.)\h'.
				'(.+?(?:\n+|$))((?:(?: {4}|\t)+.+?(?:\n+|$))*)/s',
				substr($str,$ptr),$match)) {
				$match[3]=preg_replace('/(?<=^|\n)(?: {4}|\t)/','',$match[3]);
				$found=FALSE;
				foreach (array_slice($this->blocks,0,-1) as $regex)
					if (preg_match($regex,$match[3])) {
						$found=TRUE;
						break;
					}
				// List
				if ($first) {
					// First pass
					if (is_numeric($match[1]))
						$type='ol';
					if (preg_match('/\n{2,}$/',$match[2].
						($found?'':$match[3])))
						// Loose structure; Use paragraphs
						$tight=FALSE;
					$first=FALSE;
				}
				// Strip leading whitespaces
				$ptr+=strlen($match[0]);
				$tmp=$this->snip($match[2].$match[3]);
				if ($tight) {
					if ($found)
						$tmp=$match[2].$this->build($this->snip($match[3]));
				}
				else
					$tmp=$this->build($tmp);
				$dst.='<li>'.$this->scan(trim($tmp)).'</li>'."\n";
			}
		}
		return strlen($dst)?
			('<'.$type.'>'."\n".$dst.'</'.$type.'>'."\n\n"):'';
	}

	/**
	*	Ignore raw HTML
	*	@return string
	*	@param $str string
	**/
	protected function _raw($str) {
		return $str;
	}

	/**
	*	Process paragraph
	*	@return string
	*	@param $str string
	**/
	protected function _p($str) {
		$str=trim($str);
		if (strlen($str)) {
			if (preg_match('/^(.+?\n)([>#].+)$/s',$str,$parts))
				return $this->_p($parts[1]).$this->build($parts[2]);
			$str=preg_replace_callback(
				'/([^<>\[]+)?(<[\?%].+?[\?%]>|<.+?>|\[.+?\]\s*\(.+?\))|'.
				'(.+)/s',
				function($expr) {
					$tmp='';
					if (isset($expr[4]))
						$tmp.=$this->esc($expr[4]);
					else {
						if (isset($expr[1]))
							$tmp.=$this->esc($expr[1]);
						$tmp.=$expr[2];
						if (isset($expr[3]))
							$tmp.=$this->esc($expr[3]);
					}
					return $tmp;
				},
				$str
			);
			$str=preg_replace('/\s{2}\r?\n/','<br />',$str);
			return '<p>'.$this->scan($str).'</p>'."\n\n";
		}
		return '';
	}

	/**
	*	Process strong/em/strikethrough spans
	*	@return string
	*	@param $str string
	**/
	protected function _text($str) {
		$tmp='';
		while ($str!=$tmp)
			$str=preg_replace_callback(
				'/(?<=\s|^)(?<!\\\\)([*_]{1,3})(.*?)(?!\\\\)\1(?=[\s[:punct:]]|$)/',
				function($expr) {
					switch (strlen($expr[1])) {
						case 1:
							return '<em>'.$expr[2].'</em>';
						case 2:
							return '<strong>'.$expr[2].'</strong>';
						case 3:
							return '<strong><em>'.$expr[2].'</em></strong>';
					}
				},
				preg_replace(
					'/(?<!\\\\)~~(.*?)(?!\\\\)~~(?=[\s[:punct:]]|$)/',
					'<del>\1</del>',
					$tmp=$str
				)
			);
		return $str;
	}

	/**
	*	Process image span
	*	@return string
	*	@param $str string
	**/
	protected function _img($str) {
		return preg_replace_callback(
			'/!(?:\[(.+?)\])?\h*\(<?(.*?)>?(?:\h*"(.*?)"\h*)?\)/',
			function($expr) {
				return '<img src="'.$expr[2].'"'.
					(empty($expr[1])?
						'':
						(' alt="'.$this->esc($expr[1]).'"')).
					(empty($expr[3])?
						'':
						(' title="'.$this->esc($expr[3]).'"')).' />';
			},
			$str
		);
	}

	/**
	*	Process anchor span
	*	@return string
	*	@param $str string
	**/
	protected function _a($str) {
		return preg_replace_callback(
			'/(?<!\\\\)\[(.+?)(?!\\\\)\]\h*\(<?(.*?)>?(?:\h*"(.*?)"\h*)?\)/',
			function($expr) {
				return '<a href="'.$this->esc($expr[2]).'"'.
					(empty($expr[3])?
						'':
						(' title="'.$this->esc($expr[3]).'"')).
					'>'.$this->scan($expr[1]).'</a>';
			},
			$str
		);
	}

	/**
	*	Auto-convert links
	*	@return string
	*	@param $str string
	**/
	protected function _auto($str) {
		return preg_replace_callback(
			'/`.*?<(.+?)>.*?`|<(.+?)>/',
			function($expr) {
				if (empty($expr[1]) && parse_url($expr[2],PHP_URL_SCHEME)) {
					$expr[2]=$this->esc($expr[2]);
					return '<a href="'.$expr[2].'">'.$expr[2].'</a>';
				}
				return $expr[0];
			},
			$str
		);
	}

	/**
	*	Process code span
	*	@return string
	*	@param $str string
	**/
	protected function _code($str) {
		return preg_replace_callback(
			'/`` (.+?) ``|(?<!\\\\)`(.+?)(?!\\\\)`/',
			function($expr) {
				return '<code>'.
					$this->esc(empty($expr[1])?$expr[2]:$expr[1]).'</code>';
			},
			$str
		);
	}

	/**
	*	Convert characters to HTML entities
	*	@return string
	*	@param $str string
	**/
	function esc($str) {
		if (!$this->special)
			$this->special=[
				'...'=>'&hellip;',
				'(tm)'=>'&trade;',
				'(r)'=>'&reg;',
				'(c)'=>'&copy;'
			];
		foreach ($this->special as $key=>$val)
			$str=preg_replace('/'.preg_quote($key,'/').'/i',$val,$str);
		return htmlspecialchars($str,ENT_COMPAT,
			Base::instance()->ENCODING,FALSE);
	}

	/**
	*	Reduce multiple line feeds
	*	@return string
	*	@param $str string
	**/
	protected function snip($str) {
		return preg_replace('/(?:(?<=\n)\n+)|\n+$/',"\n",$str);
	}

	/**
	*	Scan line for convertible spans
	*	@return string
	*	@param $str string
	**/
	function scan($str) {
		$inline=['img','a','text','auto','code'];
		foreach ($inline as $func)
			$str=$this->{'_'.$func}($str);
		return $str;
	}

	/**
	*	Assemble blocks
	*	@return string
	*	@param $str string
	**/
	protected function build($str) {
		if (!$this->blocks) {
			// Regexes for capturing entire blocks
			$this->blocks=[
				'blockquote'=>'/^(?:\h?>\h?.*?(?:\n+|$))+/',
				'pre'=>'/^(?:(?: {4}|\t).+?(?:\n+|$))+/',
				'fence'=>'/^`{3}\h*(\w+)?.*?[^\n]*\n+(.+?)`{3}[^\n]*'.
					'(?:\n+|$)/s',
				'hr'=>'/^\h*[*_-](?:\h?[\*_-]){2,}\h*(?:\n+|$)/',
				'atx'=>'/^\h*(#{1,6})\h?(.+?)\h*(?:#.*)?(?:\n+|$)/',
				'setext'=>'/^\h*(.+?)\h*\n([=-])+\h*(?:\n+|$)/',
				'li'=>'/^(?:(?:[*+-]|\d+\.)\h.+?(?:\n+|$)'.
					'(?:(?: {4}|\t)+.+?(?:\n+|$))*)+/s',
				'raw'=>'/^((?:<!--.+?-->|'.
					'<(address|article|aside|audio|blockquote|canvas|dd|'.
					'div|dl|fieldset|figcaption|figure|footer|form|h\d|'.
					'header|hgroup|hr|noscript|object|ol|output|p|pre|'.
					'section|table|tfoot|ul|video).*?'.
					'(?:\/>|>(?:(?>[^><]+)|(?R))*<\/\2>))'.
					'\h*(?:\n{2,}|\n*$)|<[\?%].+?[\?%]>\h*(?:\n?$|\n*))/s',
				'p'=>'/^(.+?(?:\n{2,}|\n*$))/s'
			];
		}
		// Treat lines with nothing but whitespaces as empty lines
		$str=preg_replace('/\n\h+(?=\n)/',"\n",$str);
		// Initialize block parser
		$len=strlen($str);
		$ptr=0;
		$dst='';
		// Main loop
		while ($ptr<$len) {
			if (preg_match('/^ {0,3}\[([^\[\]]+)\]:\s*<?(.*?)>?\s*'.
				'(?:"([^\n]*)")?(?:\n+|$)/s',substr($str,$ptr),$match)) {
				// Reference-style link; Backtrack
				$ptr+=strlen($match[0]);
				$tmp='';
				// Catch line breaks in title attribute
				$ref=preg_replace('/\h/','\s',preg_quote($match[1],'/'));
				while ($dst!=$tmp) {
					$dst=preg_replace_callback(
						'/(?<!\\\\)\[('.$ref.')(?!\\\\)\]\s*\[\]|'.
						'(!?)(?:\[([^\[\]]+)\]\s*)?'.
						'(?<!\\\\)\[('.$ref.')(?!\\\\)\]/',
						function($expr) use($match) {
							return (empty($expr[2]))?
								// Anchor
								('<a href="'.$this->esc($match[2]).'"'.
								(empty($match[3])?
									'':
									(' title="'.
										$this->esc($match[3]).'"')).'>'.
								// Link
								$this->scan(
									empty($expr[3])?
										(empty($expr[1])?
											$expr[4]:
											$expr[1]):
										$expr[3]
								).'</a>'):
								// Image
								('<img src="'.$match[2].'"'.
								(empty($expr[2])?
									'':
									(' alt="'.
										$this->esc($expr[3]).'"')).
								(empty($match[3])?
									'':
									(' title="'.
										$this->esc($match[3]).'"')).
								' />');
						},
						$tmp=$dst
					);
				}
			}
			else
				foreach ($this->blocks as $func=>$regex)
					if (preg_match($regex,substr($str,$ptr),$match)) {
						$ptr+=strlen($match[0]);
						$dst.=call_user_func_array(
							[$this,'_'.$func],
							count($match)>1?array_slice($match,1):$match
						);
						break;
					}
		}
		return $dst;
	}

	/**
	*	Render HTML equivalent of markdown
	*	@return string
	*	@param $txt string
	**/
	function convert($txt) {
		$txt=preg_replace_callback(
			'/(<code.*?>.+?<\/code>|'.
			'<[^>\n]+>|\([^\n\)]+\)|"[^"\n]+")|'.
			'\\\\(.)/s',
			function($expr) {
				// Process escaped characters
				return empty($expr[1])?$expr[2]:$expr[1];
			},
			$this->build(preg_replace('/\r\n|\r/',"\n",$txt))
		);
		return $this->snip($txt);
	}

}
diff --git a/vendor/fatfree-3.6/lib/matrix.php b/vendor/fatfree-3.6/lib/matrix.php
new file mode 100755
index 0000000..f3d7182
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/matrix.php
@@ -0,0 +1,111 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Generic array utilities
class Matrix extends Prefab {

	/**
	*	Retrieve values from a specified column of a multi-dimensional
	*	array variable
	*	@return array
	*	@param $var array
	*	@param $col mixed
	**/
	function pick(array $var,$col) {
		return array_map(
			function($row) use($col) {
				return $row[$col];
			},
			$var
		);
	}

	/**
	*	Rotate a two-dimensional array variable
	*	@return NULL
	*	@param $var array
	**/
	function transpose(array &$var) {
		$out=[];
		foreach ($var as $keyx=>$cols)
			foreach ($cols as $keyy=>$valy)
				$out[$keyy][$keyx]=$valy;
		$var=$out;
	}

	/**
	*	Sort a multi-dimensional array variable on a specified column
	*	@return bool
	*	@param $var array
	*	@param $col mixed
	*	@param $order int
	**/
	function sort(array &$var,$col,$order=SORT_ASC) {
		uasort(
			$var,
			function($val1,$val2) use($col,$order) {
				list($v1,$v2)=[$val1[$col],$val2[$col]];
				$out=is_numeric($v1) && is_numeric($v2)?
					Base::instance()->sign($v1-$v2):strcmp($v1,$v2);
				if ($order==SORT_DESC)
					$out=-$out;
				return $out;
			}
		);
		$var=array_values($var);
	}

	/**
	*	Change the key of a two-dimensional array element
	*	@return NULL
	*	@param $var array
	*	@param $old string
	*	@param $new string
	**/
	function changekey(array &$var,$old,$new) {
		$keys=array_keys($var);
		$vals=array_values($var);
		$keys[array_search($old,$keys)]=$new;
		$var=array_combine($keys,$vals);
	}

	/**
	*	Return month calendar of specified date, with optional setting for
	*	first day of week (0 for Sunday)
	*	@return array
	*	@param $date string
	*	@param $first int
	**/
	function calendar($date='now',$first=0) {
		$out=FALSE;
		if (extension_loaded('calendar')) {
			$parts=getdate(strtotime($date));
			$days=cal_days_in_month(CAL_GREGORIAN,$parts['mon'],$parts['year']);
			$ref=date('w',strtotime(date('Y-m',$parts[0]).'-01'))+(7-$first)%7;
			$out=[];
			for ($i=0;$i<$days;$i++)
				$out[floor(($ref+$i)/7)][($ref+$i)%7]=$i+1;
		}
		return $out;
	}

}
diff --git a/vendor/fatfree-3.6/lib/session.php b/vendor/fatfree-3.6/lib/session.php
new file mode 100755
index 0000000..3434f8d
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/session.php
@@ -0,0 +1,192 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Cache-based session handler
class Session {

	protected
		//! Session ID
		$sid,
		//! Anti-CSRF token
		$_csrf,
		//! User agent
		$_agent,
		//! IP,
		$_ip,
		//! Suspect callback
		$onsuspect,
		//! Cache instance
		$_cache;

	/**
	*	Open session
	*	@return TRUE
	*	@param $path string
	*	@param $name string
	**/
	function open($path,$name) {
		return TRUE;
	}

	/**
	*	Close session
	*	@return TRUE
	**/
	function close() {
		$this->sid=NULL;
		return TRUE;
	}

	/**
	*	Return session data in serialized format
	*	@return string
	*	@param $id string
	**/
	function read($id) {
		$this->sid=$id;
		if (!$data=$this->_cache->get($id.'.@'))
			return '';
		if ($data['ip']!=$this->_ip || $data['agent']!=$this->_agent) {
			$fw=Base::instance();
			if (!isset($this->onsuspect) ||
				$fw->call($this->onsuspect,[$this,$id])===FALSE) {
				//NB: `session_destroy` can't be called at that stage (`session_start` not completed)
				$this->destroy($id);
				$this->close();
				unset($fw->{'COOKIE.'.session_name()});
				$fw->error(403);
			}
		}
		return $data['data'];
	}

	/**
	*	Write session data
	*	@return TRUE
	*	@param $id string
	*	@param $data string
	**/
	function write($id,$data) {
		$fw=Base::instance();
		$jar=$fw->JAR;
		$this->_cache->set($id.'.@',
			[
				'data'=>$data,
				'ip'=>$this->_ip,
				'agent'=>$this->_agent,
				'stamp'=>time()
			],
			$jar['expire']
		);
		return TRUE;
	}

	/**
	*	Destroy session
	*	@return TRUE
	*	@param $id string
	**/
	function destroy($id) {
		$this->_cache->clear($id.'.@');
		return TRUE;
	}

	/**
	*	Garbage collector
	*	@return TRUE
	*	@param $max int
	**/
	function cleanup($max) {
		$this->_cache->reset('.@',$max);
		return TRUE;
	}

	/**
	 *	Return session id (if session has started)
	 *	@return string|NULL
	 **/
	function sid() {
		return $this->sid;
	}

	/**
	 *	Return anti-CSRF token
	 *	@return string
	 **/
	function csrf() {
		return $this->_csrf;
	}

	/**
	 *	Return IP address
	 *	@return string
	 **/
	function ip() {
		return $this->_ip;
	}

	/**
	 *	Return Unix timestamp
	 *	@return string|FALSE
	 **/
	function stamp() {
		if (!$this->sid)
			session_start();
		return $this->_cache->exists($this->sid.'.@',$data)?
			$data['stamp']:FALSE;
	}

	/**
	 *	Return HTTP user agent
	 *	@return string
	 **/
	function agent() {
		return $this->_agent;
	}

	/**
	*	Instantiate class
	*	@param $onsuspect callback
	*	@param $key string
	**/
	function __construct($onsuspect=NULL,$key=NULL,$cache=null) {
		$this->onsuspect=$onsuspect;
		$this->_cache=$cache?:Cache::instance();
		session_set_save_handler(
			[$this,'open'],
			[$this,'close'],
			[$this,'read'],
			[$this,'write'],
			[$this,'destroy'],
			[$this,'cleanup']
		);
		register_shutdown_function('session_commit');
		$fw=\Base::instance();
		$headers=$fw->HEADERS;
		$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
		if ($key)
			$fw->$key=$this->_csrf;
		$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
		$this->_ip=$fw->IP;
	}

}
diff --git a/vendor/fatfree-3.6/lib/smtp.php b/vendor/fatfree-3.6/lib/smtp.php
new file mode 100755
index 0000000..6a8e533
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/smtp.php
@@ -0,0 +1,353 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! SMTP plug-in
class SMTP extends Magic {

	//@{ Locale-specific error/exception messages
	const
		E_Header='%s: header is required',
		E_Blank='Message must not be blank',
		E_Attach='Attachment %s not found';
	//@}

	protected
		//! Message properties
		$headers,
		//! E-mail attachments
		$attachments,
		//! SMTP host
		$host,
		//! SMTP port
		$port,
		//! TLS/SSL
		$scheme,
		//! User ID
		$user,
		//! Password
		$pw,
		//! TLS/SSL stream context
		$context,
		//! TCP/IP socket
		$socket,
		//! Server-client conversation
		$log;

	/**
	*	Fix header
	*	@return string
	*	@param $key string
	**/
	protected function fixheader($key) {
		return str_replace(' ','-',
			ucwords(preg_replace('/[_-]/',' ',strtolower($key))));
	}

	/**
	*	Return TRUE if header exists
	*	@return bool
	*	@param $key
	**/
	function exists($key) {
		$key=$this->fixheader($key);
		return isset($this->headers[$key]);
	}

	/**
	*	Bind value to e-mail header
	*	@return string
	*	@param $key string
	*	@param $val string
	**/
	function set($key,$val) {
		$key=$this->fixheader($key);
		return $this->headers[$key]=$val;
	}

	/**
	*	Return value of e-mail header
	*	@return string|NULL
	*	@param $key string
	**/
	function &get($key) {
		$key=$this->fixheader($key);
		if (isset($this->headers[$key]))
			$val=&$this->headers[$key];
		else
			$val=NULL;
		return $val;
	}

	/**
	*	Remove header
	*	@return NULL
	*	@param $key string
	**/
	function clear($key) {
		$key=$this->fixheader($key);
		unset($this->headers[$key]);
	}

	/**
	*	Return client-server conversation history
	*	@return string
	**/
	function log() {
		return str_replace("\n",PHP_EOL,$this->log);
	}

	/**
	*	Send SMTP command and record server response
	*	@return string
	*	@param $cmd string
	*	@param $log bool|string
	*	@param $mock bool
	**/
	protected function dialog($cmd=NULL,$log=TRUE,$mock=FALSE) {
		$reply='';
		if ($mock) {
			$host=str_replace('ssl://','',$this->host);
			switch ($cmd) {
			case NULL:
				$reply='220 '.$host.' ESMTP ready'."\n";
				break;
			case 'DATA':
				$reply='354 Go ahead'."\n";
				break;
			case 'QUIT':
				$reply='221 '.$host.' closing connection'."\n";
				break;
			default:
				$reply='250 OK'."\n";
				break;
			}
		}
		else {
			$socket=&$this->socket;
			if ($cmd)
				fputs($socket,$cmd."\r\n");
			while (!feof($socket) && ($info=stream_get_meta_data($socket)) &&
				!$info['timed_out'] && $str=fgets($socket,4096)) {
				$reply.=$str;
				if (preg_match('/(?:^|\n)\d{3} .+?\r\n/s',$reply))
					break;
			}
		}
		if ($log) {
			if ($cmd)
				$this->log.=$cmd."\n";
			$this->log.=str_replace("\r",'',$reply);
		}
		return $reply;
	}

	/**
	*	Add e-mail attachment
	*	@return NULL
	*	@param $file string
	*	@param $alias string
	*	@param $cid string
	**/
	function attach($file,$alias=NULL,$cid=NULL) {
		if (!is_file($file))
			user_error(sprintf(self::E_Attach,$file),E_USER_ERROR);
		if ($alias)
			$file=[$alias,$file];
		$this->attachments[]=['filename'=>$file,'cid'=>$cid];
	}

	/**
	*	Transmit message
	*	@return bool
	*	@param $message string
	*	@param $log bool|string
	*	@param $mock bool
	**/
	function send($message,$log=TRUE,$mock=FALSE) {
		if ($this->scheme=='ssl' && !extension_loaded('openssl'))
			return FALSE;
		// Message should not be blank
		if (!$message)
			user_error(self::E_Blank,E_USER_ERROR);
		$fw=Base::instance();
		// Retrieve headers
		$headers=$this->headers;
		// Connect to the server
		if (!$mock) {
			$socket=&$this->socket;
			$socket=@stream_socket_client($this->host.':'.$this->port,
				$errno,$errstr,ini_get('default_socket_timeout'),
				STREAM_CLIENT_CONNECT,$this->context);
			if (!$socket) {
				$fw->error(500,$errstr);
				return FALSE;
			}
			stream_set_blocking($socket,TRUE);
		}
		// Get server's initial response
		$this->dialog(NULL,TRUE,$mock);
		// Announce presence
		$reply=$this->dialog('EHLO '.$fw->HOST,$log,$mock);
		if (strtolower($this->scheme)=='tls') {
			$this->dialog('STARTTLS',$log,$mock);
			if (!$mock)
				stream_socket_enable_crypto(
					$socket,TRUE,STREAM_CRYPTO_METHOD_TLS_CLIENT);
			$reply=$this->dialog('EHLO '.$fw->HOST,$log,$mock);
		}
		$message=wordwrap($message,998);
		if (preg_match('/8BITMIME/',$reply))
			$headers['Content-Transfer-Encoding']='8bit';
		else {
			$headers['Content-Transfer-Encoding']='quoted-printable';
			$message=preg_replace('/^\.(.+)/m',
				'..$1',quoted_printable_encode($message));
		}
		if ($this->user && $this->pw && preg_match('/AUTH/',$reply)) {
			// Authenticate
			$this->dialog('AUTH LOGIN',$log,$mock);
			$this->dialog(base64_encode($this->user),$log,$mock);
			$reply=$this->dialog(base64_encode($this->pw),$log,$mock);
			if (!preg_match('/^235\s.*/',$reply)) {
				$this->dialog('QUIT',$log,$mock);
				if (!$mock && $socket)
					fclose($socket);
				return FALSE;
			}
		}
		if (empty($headers['Message-Id']))
			$headers['Message-Id']='<'.uniqid('',TRUE).'@'.$this->host.'>';
		if (empty($headers['Date']))
			$headers['Date']=date('r');
		// Required headers
		$reqd=['From','To','Subject'];
		foreach ($reqd as $id)
			if (empty($headers[$id]))
				user_error(sprintf(self::E_Header,$id),E_USER_ERROR);
		$eol="\r\n";
		// Stringify headers
		foreach ($headers as $key=>&$val) {
			if (in_array($key,['From','To','Cc','Bcc'])) {
				$email='';
				preg_match_all('/(?:".+?" )?(?:<.+?>|[^ ,]+)/',
					$val,$matches,PREG_SET_ORDER);
				foreach ($matches as $raw)
					$email.=($email?', ':'').
						(preg_match('/<.+?>/',$raw[0])?
							$raw[0]:
							('<'.$raw[0].'>'));
				$val=$email;
			}
			unset($val);
		}
		// Start message dialog
		$this->dialog('MAIL FROM: '.strstr($headers['From'],'<'),$log,$mock);
		foreach ($fw->split($headers['To'].
			(isset($headers['Cc'])?(';'.$headers['Cc']):'').
			(isset($headers['Bcc'])?(';'.$headers['Bcc']):'')) as $dst) {
			$this->dialog('RCPT TO: '.strstr($dst,'<'),$log,$mock);
		}
		$this->dialog('DATA',$log,$mock);
		if ($this->attachments) {
			// Replace Content-Type
			$type=$headers['Content-Type'];
			unset($headers['Content-Type']);
			$enc=$headers['Content-Transfer-Encoding'];
			unset($headers['Content-Transfer-Encoding']);
			$hash=uniqid(NULL,TRUE);
			// Send mail headers
			$out='Content-Type: multipart/mixed; boundary="'.$hash.'"'.$eol;
			foreach ($headers as $key=>$val)
				if ($key!='Bcc')
					$out.=$key.': '.$val.$eol;
			$out.=$eol;
			$out.='This is a multi-part message in MIME format'.$eol;
			$out.=$eol;
			$out.='--'.$hash.$eol;
			$out.='Content-Type: '.$type.$eol;
			$out.='Content-Transfer-Encoding: '.$enc.$eol;
			$out.=$eol;
			$out.=$message.$eol;
			foreach ($this->attachments as $attachment) {
				if (is_array($attachment['filename']))
					list($alias,$file)=$attachment['filename'];
				else
					$alias=basename($file=$attachment['filename']);
				$out.='--'.$hash.$eol;
				$out.='Content-Type: application/octet-stream'.$eol;
				$out.='Content-Transfer-Encoding: base64'.$eol;
				if ($attachment['cid'])
					$out.='Content-Id: '.$attachment['cid'].$eol;
				$out.='Content-Disposition: attachment; '.
					'filename="'.$alias.'"'.$eol;
				$out.=$eol;
				$out.=chunk_split(base64_encode(
					file_get_contents($file))).$eol;
			}
			$out.=$eol;
			$out.='--'.$hash.'--'.$eol;
			$out.='.';
			$this->dialog($out,preg_match('/verbose/i',$log),$mock);
		}
		else {
			// Send mail headers
			$out='';
			foreach ($headers as $key=>$val)
				if ($key!='Bcc')
					$out.=$key.': '.$val.$eol;
			$out.=$eol;
			$out.=$message.$eol;
			$out.='.';
			// Send message
			$this->dialog($out,preg_match('/verbose/i',$log),$mock);
		}
		$this->dialog('QUIT',$log,$mock);
		if (!$mock && $socket)
			fclose($socket);
		return TRUE;
	}

	/**
	*	Instantiate class
	*	@param $host string
	*	@param $port int
	*	@param $scheme string
	*	@param $user string
	*	@param $pw string
	*	@param $ctx resource
	**/
	function __construct(
		$host='localhost',$port=25,$scheme=NULL,$user=NULL,$pw=NULL,$ctx=NULL) {
		$this->headers=[
			'MIME-Version'=>'1.0',
			'Content-Type'=>'text/plain; '.
				'charset='.Base::instance()->ENCODING
		];
		$this->host=strtolower((($this->scheme=strtolower($scheme))=='ssl'?
			'ssl':'tcp').'://'.$host);
		$this->port=$port;
		$this->user=$user;
		$this->pw=$pw;
		$this->context=stream_context_create($ctx);
	}

}
diff --git a/vendor/fatfree-3.6/lib/template.php b/vendor/fatfree-3.6/lib/template.php
new file mode 100755
index 0000000..919d1c6
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/template.php
@@ -0,0 +1,353 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! XML-style template engine
class Template extends Preview {

	//@{ Error messages
	const
		E_Method='Call to undefined method %s()';
	//@}

	protected
		//! Template tags
		$tags,
		//! Custom tag handlers
		$custom=[];

	/**
	*	Template -set- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _set(array $node) {
		$out='';
		foreach ($node['@attrib'] as $key=>$val)
			$out.='$'.$key.'='.
				(preg_match('/\{\{(.+?)\}\}/',$val)?
					$this->token($val):
					Base::instance()->stringify($val)).'; ';
		return '<?php '.$out.'?>';
	}

	/**
	*	Template -include- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _include(array $node) {
		$attrib=$node['@attrib'];
		$hive=isset($attrib['with']) &&
			($attrib['with']=$this->token($attrib['with'])) &&
			preg_match_all('/(\w+)\h*=\h*(.+?)(?=,|$)/',
				$attrib['with'],$pairs,PREG_SET_ORDER)?
					('['.implode(',',
						array_map(function($pair) {
							return '\''.$pair[1].'\'=>'.
								(preg_match('/^\'.*\'$/',$pair[2]) ||
									preg_match('/\$/',$pair[2])?
									$pair[2]:Base::instance()->stringify(
										Base::instance()->cast($pair[2])));
						},$pairs)).']+get_defined_vars()'):
					'get_defined_vars()';
		$ttl=isset($attrib['ttl'])?(int)$attrib['ttl']:0;
		return
			'<?php '.(isset($attrib['if'])?
				('if ('.$this->token($attrib['if']).') '):'').
				('echo $this->render('.
					(preg_match('/^\{\{(.+?)\}\}$/',$attrib['href'])?
						$this->token($attrib['href']):
						Base::instance()->stringify($attrib['href'])).','.
					'NULL,'.$hive.','.$ttl.'); ?>');
	}

	/**
	*	Template -exclude- tag handler
	*	@return string
	**/
	protected function _exclude() {
		return '';
	}

	/**
	*	Template -ignore- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _ignore(array $node) {
		return $node[0];
	}

	/**
	*	Template -loop- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _loop(array $node) {
		$attrib=$node['@attrib'];
		unset($node['@attrib']);
		return
			'<?php for ('.
				$this->token($attrib['from']).';'.
				$this->token($attrib['to']).';'.
				$this->token($attrib['step']).'): ?>'.
				$this->build($node).
			'<?php endfor; ?>';
	}

	/**
	*	Template -repeat- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _repeat(array $node) {
		$attrib=$node['@attrib'];
		unset($node['@attrib']);
		return
			'<?php '.
				(isset($attrib['counter'])?
					(($ctr=$this->token($attrib['counter'])).'=0; '):'').
				'foreach (('.
				$this->token($attrib['group']).'?:[]) as '.
				(isset($attrib['key'])?
					($this->token($attrib['key']).'=>'):'').
				$this->token($attrib['value']).'):'.
				(isset($ctr)?(' '.$ctr.'++;'):'').' ?>'.
				$this->build($node).
			'<?php endforeach; ?>';
	}

	/**
	*	Template -check- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _check(array $node) {
		$attrib=$node['@attrib'];
		unset($node['@attrib']);
		// Grab <true> and <false> blocks
		foreach ($node as $pos=>$block)
			if (isset($block['true']))
				$true=[$pos,$block];
			elseif (isset($block['false']))
				$false=[$pos,$block];
		if (isset($true,$false) && $true[0]>$false[0])
			// Reverse <true> and <false> blocks
			list($node[$true[0]],$node[$false[0]])=[$false[1],$true[1]];
		return
			'<?php if ('.$this->token($attrib['if']).'): ?>'.
				$this->build($node).
			'<?php endif; ?>';
	}

	/**
	*	Template -true- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _true(array $node) {
		return $this->build($node);
	}

	/**
	*	Template -false- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _false(array $node) {
		return '<?php else: ?>'.$this->build($node);
	}

	/**
	*	Template -switch- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _switch(array $node) {
		$attrib=$node['@attrib'];
		unset($node['@attrib']);
		foreach ($node as $pos=>$block)
			if (is_string($block) && !preg_replace('/\s+/','',$block))
				unset($node[$pos]);
		return
			'<?php switch ('.$this->token($attrib['expr']).'): ?>'.
				$this->build($node).
			'<?php endswitch; ?>';
	}

	/**
	*	Template -case- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _case(array $node) {
		$attrib=$node['@attrib'];
		unset($node['@attrib']);
		return
			'<?php case '.(preg_match('/\{\{(.+?)\}\}/',$attrib['value'])?
				$this->token($attrib['value']):
				Base::instance()->stringify($attrib['value'])).': ?>'.
				$this->build($node).
			'<?php '.(isset($attrib['break'])?
				'if ('.$this->token($attrib['break']).') ':'').
				'break; ?>';
	}

	/**
	*	Template -default- tag handler
	*	@return string
	*	@param $node array
	**/
	protected function _default(array $node) {
		return
			'<?php default: ?>'.
				$this->build($node).
			'<?php break; ?>';
	}

	/**
	*	Assemble markup
	*	@return string
	*	@param $node array|string
	**/
	function build($node) {
		if (is_string($node))
			return parent::build($node);
		$out='';
		foreach ($node as $key=>$val)
			$out.=is_int($key)?$this->build($val):$this->{'_'.$key}($val);
		return $out;
	}

	/**
	*	Extend template with custom tag
	*	@return NULL
	*	@param $tag string
	*	@param $func callback
	**/
	function extend($tag,$func) {
		$this->tags.='|'.$tag;
		$this->custom['_'.$tag]=$func;
	}

	/**
	*	Call custom tag handler
	*	@return string|FALSE
	*	@param $func string
	*	@param $args array
	**/
	function __call($func,array $args) {
		if ($func[0]=='_')
			return call_user_func_array($this->custom[$func],$args);
		if (method_exists($this,$func))
			return call_user_func_array([$this,$func],$args);
		user_error(sprintf(self::E_Method,$func),E_USER_ERROR);
	}

	/**
	*	Parse string for template directives and tokens
	*	@return array
	*	@param $text string
	**/
	function parse($text) {
		$text=parent::parse($text);
		// Build tree structure
		for ($ptr=0,$w=5,$len=strlen($text),$tree=[],$tmp='';$ptr<$len;)
			if (preg_match('/^(.{0,'.$w.'}?)<(\/?)(?:F3:)?'.
				'('.$this->tags.')\b((?:\s+[\w-.:@!]+'.
				'(?:\h*=\h*(?:"(?:.*?)"|\'(?:.*?)\'))?|'.
				'\h*\{\{.+?\}\})*)\h*(\/?)>/is',
				substr($text,$ptr),$match)) {
				if (strlen($tmp) || $match[1])
					$tree[]=$tmp.$match[1];
				// Element node
				if ($match[2]) {
					// Find matching start tag
					$stack=[];
					for($i=count($tree)-1;$i>=0;$i--) {
						$item=$tree[$i];
						if (is_array($item) &&
							array_key_exists($match[3],$item) &&
							!isset($item[$match[3]][0])) {
							// Start tag found
							$tree[$i][$match[3]]+=array_reverse($stack);
							$tree=array_slice($tree,0,$i+1);
							break;
						}
						else $stack[]=$item;
					}
				}
				else {
					// Start tag
					$node=&$tree[][$match[3]];
					$node=[];
					if ($match[4]) {
						// Process attributes
						preg_match_all(
							'/(?:(\{\{.+?\}\})|([^\s\/"\'=]+))'.
							'\h*(?:=\h*(?:"(.*?)"|\'(.*?)\'))?/s',
							$match[4],$attr,PREG_SET_ORDER);
						foreach ($attr as $kv)
							if (!empty($kv[1]) && !isset($kv[3]) && !isset($kv[4]))
								$node['@attrib'][]=$kv[1];
							else
								$node['@attrib'][$kv[1]?:$kv[2]]=
									(isset($kv[3]) && $kv[3]!==''?
										$kv[3]:
										(isset($kv[4]) && $kv[4]!==''?
											$kv[4]:NULL));
					}
				}
				$tmp='';
				$ptr+=strlen($match[0]);
				$w=5;
			}
			else {
				// Text node
				$tmp.=substr($text,$ptr,$w);
				$ptr+=$w;
				if ($w<50)
					$w++;
			}
		if (strlen($tmp))
			// Append trailing text
			$tree[]=$tmp;
		// Break references
		unset($node);
		return $tree;
	}

	/**
	*	Class constructor
	*	return object
	**/
	function __construct() {
		$ref=new ReflectionClass(get_called_class());
		$this->tags='';
		foreach ($ref->getmethods() as $method)
			if (preg_match('/^_(?=[[:alpha:]])/',$method->name))
				$this->tags.=(strlen($this->tags)?'|':'').
					substr($method->name,1);
		parent::__construct();
	}

}
diff --git a/vendor/fatfree-3.6/lib/test.php b/vendor/fatfree-3.6/lib/test.php
new file mode 100755
index 0000000..721faf7
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/test.php
@@ -0,0 +1,96 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Unit test kit
class Test {

	//@{ Reporting level
	const
		FLAG_False=0,
		FLAG_True=1,
		FLAG_Both=2;
	//@}

	protected
		//! Test results
		$data=[],
		//! Success indicator
		$passed=TRUE;

	/**
	*	Return test results
	*	@return array
	**/
	function results() {
		return $this->data;
	}

	/**
	*	Return FALSE if at least one test case fails
	*	@return bool
	**/
	function passed() {
		return $this->passed;
	}

	/**
	*	Evaluate condition and save test result
	*	@return object
	*	@param $cond bool
	*	@param $text string
	**/
	function expect($cond,$text=NULL) {
		$out=(bool)$cond;
		if ($this->level==$out || $this->level==self::FLAG_Both) {
			$data=['status'=>$out,'text'=>$text,'source'=>NULL];
			foreach (debug_backtrace() as $frame)
				if (isset($frame['file'])) {
					$data['source']=Base::instance()->
						fixslashes($frame['file']).':'.$frame['line'];
					break;
				}
			$this->data[]=$data;
		}
		if (!$out && $this->passed)
			$this->passed=FALSE;
		return $this;
	}

	/**
	*	Append message to test results
	*	@return NULL
	*	@param $text string
	**/
	function message($text) {
		$this->expect(TRUE,$text);
	}

	/**
	*	Class constructor
	*	@return NULL
	*	@param $level int
	**/
	function __construct($level=self::FLAG_Both) {
		$this->level=$level;
	}

}
diff --git a/vendor/fatfree-3.6/lib/utf.php b/vendor/fatfree-3.6/lib/utf.php
new file mode 100755
index 0000000..a010c66
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/utf.php
@@ -0,0 +1,199 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Unicode string manager
class UTF extends Prefab {

	/**
	*	Get string length
	*	@return int
	*	@param $str string
	**/
	function strlen($str) {
		preg_match_all('/./us',$str,$parts);
		return count($parts[0]);
	}

	/**
	*	Reverse a string
	*	@return string
	*	@param $str string
	**/
	function strrev($str) {
		preg_match_all('/./us',$str,$parts);
		return implode('',array_reverse($parts[0]));
	}

	/**
	*	Find position of first occurrence of a string (case-insensitive)
	*	@return int|FALSE
	*	@param $stack string
	*	@param $needle string
	*	@param $ofs int
	**/
	function stripos($stack,$needle,$ofs=0) {
		return $this->strpos($stack,$needle,$ofs,TRUE);
	}

	/**
	*	Find position of first occurrence of a string
	*	@return int|FALSE
	*	@param $stack string
	*	@param $needle string
	*	@param $ofs int
	*	@param $case bool
	**/
	function strpos($stack,$needle,$ofs=0,$case=FALSE) {
		return preg_match('/^(.{'.$ofs.'}.*?)'.
			preg_quote($needle,'/').'/us'.($case?'i':''),$stack,$match)?
			$this->strlen($match[1]):FALSE;
	}

	/**
	*	Returns part of haystack string from the first occurrence of
	*	needle to the end of haystack (case-insensitive)
	*	@return string|FALSE
	*	@param $stack string
	*	@param $needle string
	*	@param $before bool
	**/
	function stristr($stack,$needle,$before=FALSE) {
		return $this->strstr($stack,$needle,$before,TRUE);
	}

	/**
	*	Returns part of haystack string from the first occurrence of
	*	needle to the end of haystack
	*	@return string|FALSE
	*	@param $stack string
	*	@param $needle string
	*	@param $before bool
	*	@param $case bool
	**/
	function strstr($stack,$needle,$before=FALSE,$case=FALSE) {
		if (!$needle)
			return FALSE;
		preg_match('/^(.*?)'.preg_quote($needle,'/').'/us'.($case?'i':''),
			$stack,$match);
		return isset($match[1])?
			($before?
				$match[1]:
				$this->substr($stack,$this->strlen($match[1]))):
			FALSE;
	}

	/**
	*	Return part of a string
	*	@return string|FALSE
	*	@param $str string
	*	@param $start int
	*	@param $len int
	**/
	function substr($str,$start,$len=0) {
		if ($start<0)
			$start=$this->strlen($str)+$start;
		if (!$len)
			$len=$this->strlen($str)-$start;
		return preg_match('/^.{'.$start.'}(.{0,'.$len.'})/us',$str,$match)?
			$match[1]:FALSE;
	}

	/**
	*	Count the number of substring occurrences
	*	@return int
	*	@param $stack string
	*	@param $needle string
	**/
	function substr_count($stack,$needle) {
		preg_match_all('/'.preg_quote($needle,'/').'/us',$stack,
			$matches,PREG_SET_ORDER);
		return count($matches);
	}

	/**
	*	Strip whitespaces from the beginning of a string
	*	@return string
	*	@param $str string
	**/
	function ltrim($str) {
		return preg_replace('/^[\pZ\pC]+/u','',$str);
	}

	/**
	*	Strip whitespaces from the end of a string
	*	@return string
	*	@param $str string
	**/
	function rtrim($str) {
		return preg_replace('/[\pZ\pC]+$/u','',$str);
	}

	/**
	*	Strip whitespaces from the beginning and end of a string
	*	@return string
	*	@param $str string
	**/
	function trim($str) {
		return preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u','',$str);
	}

	/**
	*	Return UTF-8 byte order mark
	*	@return string
	**/
	function bom() {
		return chr(0xef).chr(0xbb).chr(0xbf);
	}

	/**
	*	Convert code points to Unicode symbols
	*	@return string
	*	@param $str string
	**/
	function translate($str) {
		return html_entity_decode(
			preg_replace('/\\\\u([[:xdigit:]]+)/i','&#x\1;',$str));
	}

	/**
	*	Translate emoji tokens to Unicode font-supported symbols
	*	@return string
	*	@param $str string
	**/
	function emojify($str) {
		$map=[
			':('=>'\u2639', // frown
			':)'=>'\u263a', // smile
			'<3'=>'\u2665', // heart
			':D'=>'\u1f603', // grin
			'XD'=>'\u1f606', // laugh
			';)'=>'\u1f609', // wink
			':P'=>'\u1f60b', // tongue
			':,'=>'\u1f60f', // think
			':/'=>'\u1f623', // skeptic
			'8O'=>'\u1f632', // oops
		]+Base::instance()->EMOJI;
		return $this->translate(str_replace(array_keys($map),
			array_values($map),$str));
	}

}
diff --git a/vendor/fatfree-3.6/lib/web.php b/vendor/fatfree-3.6/lib/web.php
new file mode 100755
index 0000000..36812ee
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/web.php
@@ -0,0 +1,932 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

//! Wrapper for various HTTP utilities
class Web extends Prefab {

	//@{ Error messages
	const
		E_Request='No suitable HTTP request engine found';
	//@}

	protected
		//! HTTP request engine
		$wrapper;

	/**
	*	Detect MIME type using file extension
	*	@return string
	*	@param $file string
	**/
	function mime($file) {
		if (preg_match('/\w+$/',$file,$ext)) {
			$map=[
				'au'=>'audio/basic',
				'avi'=>'video/avi',
				'bmp'=>'image/bmp',
				'bz2'=>'application/x-bzip2',
				'css'=>'text/css',
				'dtd'=>'application/xml-dtd',
				'doc'=>'application/msword',
				'gif'=>'image/gif',
				'gz'=>'application/x-gzip',
				'hqx'=>'application/mac-binhex40',
				'html?'=>'text/html',
				'jar'=>'application/java-archive',
				'jpe?g|jfif?'=>'image/jpeg',
				'js'=>'application/x-javascript',
				'midi'=>'audio/x-midi',
				'mp3'=>'audio/mpeg',
				'mpe?g'=>'video/mpeg',
				'ogg'=>'audio/vorbis',
				'pdf'=>'application/pdf',
				'png'=>'image/png',
				'ppt'=>'application/vnd.ms-powerpoint',
				'ps'=>'application/postscript',
				'qt'=>'video/quicktime',
				'ram?'=>'audio/x-pn-realaudio',
				'rdf'=>'application/rdf',
				'rtf'=>'application/rtf',
				'sgml?'=>'text/sgml',
				'sit'=>'application/x-stuffit',
				'svg'=>'image/svg+xml',
				'swf'=>'application/x-shockwave-flash',
				'tgz'=>'application/x-tar',
				'tiff'=>'image/tiff',
				'txt'=>'text/plain',
				'wav'=>'audio/wav',
				'xls'=>'application/vnd.ms-excel',
				'xml'=>'application/xml',
				'zip'=>'application/x-zip-compressed'
			];
			foreach ($map as $key=>$val)
				if (preg_match('/'.$key.'/',strtolower($ext[0])))
					return $val;
		}
		return 'application/octet-stream';
	}

	/**
	*	Return the MIME types stated in the HTTP Accept header as an array;
	*	If a list of MIME types is specified, return the best match; or
	*	FALSE if none found
	*	@return array|string|FALSE
	*	@param $list string|array
	**/
	function acceptable($list=NULL) {
		$accept=[];
		foreach (explode(',',str_replace(' ','',@$_SERVER['HTTP_ACCEPT']))
			as $mime)
			if (preg_match('/(.+?)(?:;q=([\d\.]+)|$)/',$mime,$parts))
				$accept[$parts[1]]=isset($parts[2])?$parts[2]:1;
		if (!$accept)
			$accept['*/*']=1;
		else {
			krsort($accept);
			arsort($accept);
		}
		if ($list) {
			if (is_string($list))
				$list=explode(',',$list);
			foreach ($accept as $mime=>$q)
				if ($q && $out=preg_grep('/'.
					str_replace('\*','.*',preg_quote($mime,'/')).'/',$list))
					return current($out);
			return FALSE;
		}
		return $accept;
	}

	/**
	*	Transmit file to HTTP client; Return file size if successful,
	*	FALSE otherwise
	*	@return int|FALSE
	*	@param $file string
	*	@param $mime string
	*	@param $kbps int
	*	@param $force bool
	*	@param $name string
	*	@param $flush bool
	**/
	function send($file,$mime=NULL,$kbps=0,$force=TRUE,$name=NULL,$flush=TRUE) {
		if (!is_file($file))
			return FALSE;
		$size=filesize($file);
		if (PHP_SAPI!='cli') {
			header('Content-Type: '.($mime?:$this->mime($file)));
			if ($force)
				header('Content-Disposition: attachment; '.
					'filename="'.($name!==NULL?$name:basename($file)).'"');
			header('Accept-Ranges: bytes');
			header('Content-Length: '.$size);
			header('X-Powered-By: '.Base::instance()->PACKAGE);
		}
		if (!$kbps && $flush) {
			while (ob_get_level())
				ob_end_clean();
			readfile($file);
		}
		else {
			$ctr=0;
			$handle=fopen($file,'rb');
			$start=microtime(TRUE);
			while (!feof($handle) &&
				($info=stream_get_meta_data($handle)) &&
				!$info['timed_out'] && !connection_aborted()) {
				if ($kbps) {
					// Throttle output
					$ctr++;
					if ($ctr/$kbps>$elapsed=microtime(TRUE)-$start)
						usleep(1e6*($ctr/$kbps-$elapsed));
				}
				// Send 1KiB and reset timer
				echo fread($handle,1024);
				if ($flush) {
					ob_flush();
					flush();
				}
			}
			fclose($handle);
		}
		return $size;
	}

	/**
	*	Receive file(s) from HTTP client
	*	@return array|bool
	*	@param $func callback
	*	@param $overwrite bool
	*	@param $slug callback|bool
	**/
	function receive($func=NULL,$overwrite=FALSE,$slug=TRUE) {
		$fw=Base::instance();
		$dir=$fw->UPLOADS;
		if (!is_dir($dir))
			mkdir($dir,Base::MODE,TRUE);
		if ($fw->VERB=='PUT') {
			$tmp=$fw->TEMP.$fw->SEED.'.'.$fw->hash(uniqid());
			if (!$fw->RAW)
				$fw->write($tmp,$fw->BODY);
			else {
				$src=@fopen('php://input','r');
				$dst=@fopen($tmp,'w');
				if (!$src || !$dst)
					return FALSE;
				while (!feof($src) &&
					($info=stream_get_meta_data($src)) &&
					!$info['timed_out'] && $str=fgets($src,4096))
					fputs($dst,$str,strlen($str));
				fclose($dst);
				fclose($src);
			}
			$base=basename($fw->URI);
			$file=[
				'name'=>$dir.
					($slug && preg_match('/(.+?)(\.\w+)?$/',$base,$parts)?
						(is_callable($slug)?
							$slug($base):
							($this->slug($parts[1]).
								(isset($parts[2])?$parts[2]:''))):
						$base),
				'tmp_name'=>$tmp,
				'type'=>$this->mime($base),
				'size'=>filesize($tmp)
			];
			return (!file_exists($file['name']) || $overwrite) &&
				(!$func || $fw->call($func,[$file])!==FALSE) &&
				rename($tmp,$file['name']);
		}
		$fetch=function($arr) use(&$fetch) {
			if (!is_array($arr))
				return [$arr];
			$data=[];
			foreach($arr as $k=>$sub)
				$data=array_merge($data,$fetch($sub));
			return $data;
		};
		$out=[];
		foreach ($_FILES as $name=>$item) {
			$files=[];
			foreach ($item as $k=>$mix)
				foreach ($fetch($mix) as $i=>$val)
					$files[$i][$k]=$val;
			foreach ($files as $file) {
				if (empty($file['name']))
					continue;
				$base=basename($file['name']);
				$file['name']=$dir.
					($slug && preg_match('/(.+?)(\.\w+)?$/',$base,$parts)?
						(is_callable($slug)?
							$slug($base,$name):
							($this->slug($parts[1]).
								(isset($parts[2])?$parts[2]:''))):
						$base);
				$out[$file['name']]=!$file['error'] &&
					(!file_exists($file['name']) || $overwrite) &&
					(!$func || $fw->call($func,[$file,$name])!==FALSE) &&
					move_uploaded_file($file['tmp_name'],$file['name']);
			}
		}
		return $out;
	}

	/**
	*	Return upload progress in bytes, FALSE on failure
	*	@return int|FALSE
	*	@param $id string
	**/
	function progress($id) {
		// ID returned by session.upload_progress.name
		return ini_get('session.upload_progress.enabled') &&
			isset($_SESSION[$id]['bytes_processed'])?
				$_SESSION[$id]['bytes_processed']:FALSE;
	}

	/**
	*	HTTP request via cURL
	*	@return array
	*	@param $url string
	*	@param $options array
	**/
	protected function _curl($url,$options) {
		$curl=curl_init($url);
		if (!ini_get('open_basedir'))
			curl_setopt($curl,CURLOPT_FOLLOWLOCATION,
				$options['follow_location']);
		curl_setopt($curl,CURLOPT_MAXREDIRS,
			$options['max_redirects']);
		curl_setopt($curl,CURLOPT_PROTOCOLS,CURLPROTO_HTTP|CURLPROTO_HTTPS);
		curl_setopt($curl,CURLOPT_REDIR_PROTOCOLS,CURLPROTO_HTTP|CURLPROTO_HTTPS);
		curl_setopt($curl,CURLOPT_CUSTOMREQUEST,$options['method']);
		if (isset($options['header']))
			curl_setopt($curl,CURLOPT_HTTPHEADER,$options['header']);
		if (isset($options['content']))
			curl_setopt($curl,CURLOPT_POSTFIELDS,$options['content']);
		if (isset($options['proxy']))
			curl_setopt($curl,CURLOPT_PROXY,$options['proxy']);
		curl_setopt($curl,CURLOPT_ENCODING,'gzip,deflate');
		$timeout=isset($options['timeout'])?
			$options['timeout']:
			ini_get('default_socket_timeout');
		curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,$timeout);
		curl_setopt($curl,CURLOPT_TIMEOUT,$timeout);
		$headers=[];
		curl_setopt($curl,CURLOPT_HEADERFUNCTION,
			// Callback for response headers
			function($curl,$line) use(&$headers) {
				if ($trim=trim($line))
					$headers[]=$trim;
				return strlen($line);
			}
		);
		curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,2);
		curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
		ob_start();
		curl_exec($curl);
		$err=curl_error($curl);
		curl_close($curl);
		$body=ob_get_clean();
		if (!$err &&
			$options['follow_location'] &&
			preg_grep('/HTTP\/1\.\d 3\d{2}/',$headers) &&
			preg_match('/^Location: (.+)$/m',implode(PHP_EOL,$headers),$loc)) {
			$options['max_redirects']--;
			if($loc[1][0] == '/') {
				$parts=parse_url($url);
				$loc[1]=$parts['scheme'].'://'.$parts['host'].
					((isset($parts['port']) && !in_array($parts['port'],[80,443]))
						?':'.$parts['port']:'').$loc[1];
			}
			return $this->request($loc[1],$options);
		}
		return [
			'body'=>$body,
			'headers'=>$headers,
			'engine'=>'cURL',
			'cached'=>FALSE,
			'error'=>$err
		];
	}

	/**
	*	HTTP request via PHP stream wrapper
	*	@return array
	*	@param $url string
	*	@param $options array
	**/
	protected function _stream($url,$options) {
		$eol="\r\n";
		if (isset($options['proxy'])) {
			$options['proxy']=preg_replace('/https?/i','tcp',$options['proxy']);
			$options['request_fulluri']=true;
			if (preg_match('/socks4?/i',$options['proxy']))
				return $this->_socket($url,$options);
		}
		$options['header']=implode($eol,$options['header']);
		$body=@file_get_contents($url,FALSE,
			stream_context_create(['http'=>$options]));
		$headers=isset($http_response_header)?
			$http_response_header:[];
		$err='';
		if (is_string($body)) {
			$match=NULL;
			foreach ($headers as $header)
				if (preg_match('/Content-Encoding: (.+)/',$header,$match))
					break;
			if ($match)
				switch ($match[1]) {
					case 'gzip':
						$body=gzdecode($body);
						break;
					case 'deflate':
						$body=gzuncompress($body);
						break;
				}
		}
		else {
			$tmp=error_get_last();
			$err=$tmp['message'];
		}
		return [
			'body'=>$body,
			'headers'=>$headers,
			'engine'=>'stream',
			'cached'=>FALSE,
			'error'=>$err
		];
	}

	/**
	*	HTTP request via low-level TCP/IP socket
	*	@return array
	*	@param $url string
	*	@param $options array
	**/
	protected function _socket($url,$options) {
		$eol="\r\n";
		$headers=[];
		$body='';
		$parts=parse_url($url);
		$hostname=$parts['host'];
		$proxy=false;
		if ($parts['scheme']=='https')
			$parts['host']='ssl://'.$parts['host'];
		if (empty($parts['port']))
			$parts['port']=$parts['scheme']=='https'?443:80;
		if (empty($parts['path']))
			$parts['path']='/';
		if (empty($parts['query']))
			$parts['query']='';
		if (isset($options['proxy'])) {
			$req=$url;
			$pp=parse_url($options['proxy']);
			$proxy=$pp['scheme'];
			if ($pp['scheme']=='https')
				$pp['host']='ssl://'.$pp['host'];
			if (empty($pp['port']))
				$pp['port']=$pp['scheme']=='https'?443:80;
			$socket=@fsockopen($pp['host'],$pp['port'],$code,$err);
		} else {
			$req=$parts['path'].($parts['query']?('?'.$parts['query']):'');
			$socket=@fsockopen($parts['host'],$parts['port'],$code,$err);
		}
		if ($socket) {
			stream_set_blocking($socket,TRUE);
			stream_set_timeout($socket,isset($options['timeout'])?
				$options['timeout']:ini_get('default_socket_timeout'));
			if ($proxy=='socks4') {
				// SOCKS4; http://en.wikipedia.org/wiki/SOCKS#Protocol
				$packet="\x04\x01".pack("n", $parts['port']).
					pack("H*",dechex(ip2long(gethostbyname($hostname))))."\0";
				fputs($socket, $packet, strlen($packet));
				$response=fread($socket, 9);
				if (strlen($response)==8 && (ord($response[0])==0 || ord($response[0])==4)
					&& ord($response[1])==90) {
					$options['header'][]='Host: '.$hostname;
				} else
					$err='Socket Status '.ord($response[1]);
			}
			fputs($socket,$options['method'].' '.$req.' HTTP/1.0'.$eol);
			fputs($socket,implode($eol,$options['header']).$eol.$eol);
			if (isset($options['content']))
				fputs($socket,$options['content'].$eol);
			// Get response
			$content='';
			while (!feof($socket) &&
				($info=stream_get_meta_data($socket)) &&
				!$info['timed_out'] && !connection_aborted() &&
				$str=fgets($socket,4096))
				$content.=$str;
			fclose($socket);
			$html=explode($eol.$eol,$content,2);
			$body=isset($html[1])?$html[1]:'';
			$headers=array_merge($headers,$current=explode($eol,$html[0]));
			$match=NULL;
			foreach ($current as $header)
				if (preg_match('/Content-Encoding: (.+)/',$header,$match))
					break;
			if ($match)
				switch ($match[1]) {
					case 'gzip':
						$body=gzdecode($body);
						break;
					case 'deflate':
						$body=gzuncompress($body);
						break;
				}
			if ($options['follow_location'] &&
				preg_grep('/HTTP\/1\.\d 3\d{2}/',$headers) &&
				preg_match('/Location: (.+?)'.preg_quote($eol).'/',
				$html[0],$loc)) {
				$options['max_redirects']--;
				return $this->request($loc[1],$options);
			}
		}
		return [
			'body'=>$body,
			'headers'=>$headers,
			'engine'=>'socket',
			'cached'=>FALSE,
			'error'=>$err
		];
	}

	/**
	*	Specify the HTTP request engine to use; If not available,
	*	fall back to an applicable substitute
	*	@return string
	*	@param $arg string
	**/
	function engine($arg='curl') {
		$arg=strtolower($arg);
		$flags=[
			'curl'=>extension_loaded('curl'),
			'stream'=>ini_get('allow_url_fopen'),
			'socket'=>function_exists('fsockopen')
		];
		if ($flags[$arg])
			return $this->wrapper=$arg;
		foreach ($flags as $key=>$val)
			if ($val)
				return $this->wrapper=$key;
		user_error(self::E_Request,E_USER_ERROR);
	}

	/**
	*	Replace old headers with new elements
	*	@return NULL
	*	@param $old array
	*	@param $new string|array
	**/
	function subst(array &$old,$new) {
		if (is_string($new))
			$new=[$new];
		foreach ($new as $hdr) {
			$old=preg_grep('/'.preg_quote(strstr($hdr,':',TRUE),'/').':.+/',
				$old,PREG_GREP_INVERT);
			array_push($old,$hdr);
		}
	}

	/**
	*	Submit HTTP request; Use HTTP context options (described in
	*	http://www.php.net/manual/en/context.http.php) if specified;
	*	Cache the page as instructed by remote server
	*	@return array|FALSE
	*	@param $url string
	*	@param $options array
	**/
	function request($url,array $options=NULL) {
		$fw=Base::instance();
		$parts=parse_url($url);
		if (empty($parts['scheme'])) {
			// Local URL
			$url=$fw->SCHEME.'://'.$fw->HOST.
				(in_array($fw->PORT,[80,443])?'':(':'.$fw->PORT)).
				($url[0]!='/'?($fw->BASE.'/'):'').$url;
			$parts=parse_url($url);
		}
		elseif (!preg_match('/https?/',$parts['scheme']))
			return FALSE;
		if (!is_array($options))
			$options=[];
		if (empty($options['header']))
			$options['header']=[];
		elseif (is_string($options['header']))
			$options['header']=[$options['header']];
		if (!$this->wrapper)
			$this->engine();
		if ($this->wrapper!='stream') {
			// PHP streams can't cope with redirects when Host header is set
			$this->subst($options['header'],'Host: '.$parts['host']);
		}
		$this->subst($options['header'],
			[
				'Accept-Encoding: gzip,deflate',
				'User-Agent: '.(isset($options['user_agent'])?
					$options['user_agent']:
					'Mozilla/5.0 (compatible; '.php_uname('s').')'),
				'Connection: close'
			]
		);
		if (isset($options['content']) && is_string($options['content'])) {
			if ($options['method']=='POST' &&
				!preg_grep('/^Content-Type:/',$options['header']))
				$this->subst($options['header'],
					'Content-Type: application/x-www-form-urlencoded');
			$this->subst($options['header'],
				'Content-Length: '.strlen($options['content']));
		}
		if (isset($parts['user'],$parts['pass']))
			$this->subst($options['header'],
				'Authorization: Basic '.
					base64_encode($parts['user'].':'.$parts['pass'])
			);
		$options+=[
			'method'=>'GET',
			'header'=>$options['header'],
			'follow_location'=>TRUE,
			'max_redirects'=>20,
			'ignore_errors'=>FALSE
		];
		$eol="\r\n";
		if ($fw->CACHE &&
			preg_match('/GET|HEAD/',$options['method'])) {
			$cache=Cache::instance();
			if ($cache->exists(
				$hash=$fw->hash($options['method'].' '.$url).'.url',$data)) {
				if (preg_match('/Last-Modified: (.+?)'.preg_quote($eol).'/',
					implode($eol,$data['headers']),$mod))
					$this->subst($options['header'],
						'If-Modified-Since: '.$mod[1]);
			}
		}
		$result=$this->{'_'.$this->wrapper}($url,$options);
		if ($result && isset($cache)) {
			if (preg_match('/HTTP\/1\.\d 304/',
				implode($eol,$result['headers']))) {
				$result=$cache->get($hash);
				$result['cached']=TRUE;
			}
			elseif (preg_match('/Cache-Control:(?:.*)max-age=(\d+)(?:,?.*'.
				preg_quote($eol).')/',implode($eol,$result['headers']),$exp))
				$cache->set($hash,$result,$exp[1]);
		}
		$req=[$options['method'].' '.$url];
		foreach ($options['header'] as $header)
			array_push($req,$header);
		return array_merge(['request'=>$req],$result);
	}

	/**
	*	Strip Javascript/CSS files of extraneous whitespaces and comments;
	*	Return combined output as a minified string
	*	@return string
	*	@param $files string|array
	*	@param $mime string
	*	@param $header bool
	*	@param $path string
	**/
	function minify($files,$mime=NULL,$header=TRUE,$path=NULL) {
		$fw=Base::instance();
		if (is_string($files))
			$files=$fw->split($files);
		if (!$mime)
			$mime=$this->mime($files[0]);
		preg_match('/\w+$/',$files[0],$ext);
		$cache=Cache::instance();
		$dst='';
		if (!isset($path))
			$path=$fw->UI.';./';
		foreach ($fw->split($path,FALSE) as $dir)
			foreach ($files as $file)
				if (is_file($save=$fw->fixslashes($dir.$file)) &&
					is_bool(strpos($save,'../')) &&
					preg_match('/\.(css|js)$/i',$file)) {
					if ($fw->CACHE &&
						($cached=$cache->exists(
							$hash=$fw->hash($save).'.'.$ext[0],$data)) &&
						$cached[0]>filemtime($save))
						$dst.=$data;
					else {
						$data='';
						$src=$fw->read($save);
						for ($ptr=0,$len=strlen($src);$ptr<$len;) {
							if (preg_match('/^@import\h+url'.
								'\(\h*([\'"])((?!(?:https?:)?\/\/).+?)\1\h*\)[^;]*;/',
								substr($src,$ptr),$parts)) {
								$path=dirname($file);
								$data.=$this->minify(
									($path?($path.'/'):'').$parts[2],
									$mime,$header
								);
								$ptr+=strlen($parts[0]);
								continue;
							}
							if ($src[$ptr]=='/') {
								if ($src[$ptr+1]=='*') {
									// Multiline comment
									$str=strstr(
										substr($src,$ptr+2),'*/',TRUE);
									$ptr+=strlen($str)+4;
								}
								elseif ($src[$ptr+1]=='/') {
									// Single-line comment
									$str=strstr(
										substr($src,$ptr+2),"\n",TRUE);
									$ptr+=(empty($str))?
										strlen(substr($src,$ptr)):strlen($str)+2;
								}
								else {
									// Presume it's a regex pattern
									$regex=TRUE;
									// Backtrack and validate
									for ($ofs=$ptr;$ofs;$ofs--) {
										// Pattern should be preceded by
										// open parenthesis, colon,
										// object property or operator
										if (preg_match(
											'/(return|[(:=!+\-*&|])$/',
											substr($src,0,$ofs))) {
											$data.='/';
											$ptr++;
											while ($ptr<$len) {
												$data.=$src[$ptr];
												$ptr++;
												if ($src[$ptr-1]=='\\') {
													$data.=$src[$ptr];
													$ptr++;
												}
												elseif ($src[$ptr-1]=='/')
													break;
											}
											break;
										}
										elseif (!ctype_space($src[$ofs-1])) {
											// Not a regex pattern
											$regex=FALSE;
											break;
										}
									}
									if (!$regex) {
										// Division operator
										$data.=$src[$ptr];
										$ptr++;
									}
								}
								continue;
							}
							if (in_array($src[$ptr],['\'','"'])) {
								$match=$src[$ptr];
								$data.=$match;
								$ptr++;
								// String literal
								while ($ptr<$len) {
									$data.=$src[$ptr];
									$ptr++;
									if ($src[$ptr-1]=='\\') {
										$data.=$src[$ptr];
										$ptr++;
									}
									elseif ($src[$ptr-1]==$match)
										break;
								}
								continue;
							}
							if (ctype_space($src[$ptr])) {
								if ($ptr+1<strlen($src) &&
									preg_match('/[\w'.($ext[0]=='css'?
										'#\.%+\-*()\[\]':'\$').']{2}|'.
										'[+\-]{2}/',
										substr($data,-1).$src[$ptr+1]) ||
									($ext[0]=='css' && $ptr+2<strlen($src) &&
									preg_match('/:\w/',substr($src,$ptr+1,2))))
									$data.=' ';
								$ptr++;
								continue;
							}
							$data.=$src[$ptr];
							$ptr++;
						}
						if ($ext[0]=='css')
							$data=str_replace(';}','}',$data);
						if ($fw->CACHE)
							$cache->set($hash,$data);
						$dst.=$data;
					}
				}
		if (PHP_SAPI!='cli' && $header)
			header('Content-Type: '.$mime.'; charset='.$fw->ENCODING);
		return $dst;
	}

	/**
	*	Retrieve RSS feed and return as an array
	*	@return array|FALSE
	*	@param $url string
	*	@param $max int
	*	@param $tags string
	**/
	function rss($url,$max=10,$tags=NULL) {
		if (!$data=$this->request($url))
			return FALSE;
		// Suppress errors caused by invalid XML structures
		libxml_use_internal_errors(TRUE);
		$xml=simplexml_load_string($data['body'],
			NULL,LIBXML_NOBLANKS|LIBXML_NOERROR);
		if (!is_object($xml))
			return FALSE;
		$out=[];
		if (isset($xml->channel)) {
			$out['source']=(string)$xml->channel->title;
			$max=min($max,count($xml->channel->item));
			for ($i=0;$i<$max;$i++) {
				$item=$xml->channel->item[$i];
				$list=[''=>NULL]+$item->getnamespaces(TRUE);
				$fields=[];
				foreach ($list as $ns=>$uri)
					foreach ($item->children($uri) as $key=>$val)
						$fields[$ns.($ns?':':'').$key]=(string)$val;
				$out['feed'][]=$fields;
			}
		}
		else
			return FALSE;
		Base::instance()->scrub($out,$tags);
		return $out;
	}

	/**
	*	Retrieve information from whois server
	*	@return string|FALSE
	*	@param $addr string
	*	@param $server string
	**/
	function whois($addr,$server='whois.internic.net') {
		$socket=@fsockopen($server,43,$errno,$errstr);
		if (!$socket)
			// Can't establish connection
			return FALSE;
		// Set connection timeout parameters
		stream_set_blocking($socket,FALSE);
		stream_set_timeout($socket,ini_get('default_socket_timeout'));
		// Send request
		fputs($socket,$addr."\r\n");
		$info=stream_get_meta_data($socket);
		// Get response
		$response='';
		while (!feof($socket) && !$info['timed_out']) {
			$response.=fgets($socket,4096); // MDFK97
			$info=stream_get_meta_data($socket);
		}
		fclose($socket);
		return $info['timed_out']?FALSE:trim($response);
	}

	/**
	*	Return preset diacritics translation table
	*	@return array
	**/
	function diacritics() {
		return [
			'Ǎ'=>'A','А'=>'A','Ā'=>'A','Ă'=>'A','Ą'=>'A','Å'=>'A',
			'Ǻ'=>'A','Ä'=>'Ae','Á'=>'A','À'=>'A','Ã'=>'A','Â'=>'A',
			'Æ'=>'AE','Ǽ'=>'AE','Б'=>'B','Ç'=>'C','Ć'=>'C','Ĉ'=>'C',
			'Č'=>'C','Ċ'=>'C','Ц'=>'C','Ч'=>'Ch','Ð'=>'Dj','Đ'=>'Dj',
			'Ď'=>'Dj','Д'=>'Dj','É'=>'E','Ę'=>'E','Ё'=>'E','Ė'=>'E',
			'Ê'=>'E','Ě'=>'E','Ē'=>'E','È'=>'E','Е'=>'E','Э'=>'E',
			'Ë'=>'E','Ĕ'=>'E','Ф'=>'F','Г'=>'G','Ģ'=>'G','Ġ'=>'G',
			'Ĝ'=>'G','Ğ'=>'G','Х'=>'H','Ĥ'=>'H','Ħ'=>'H','Ï'=>'I',
			'Ĭ'=>'I','İ'=>'I','Į'=>'I','Ī'=>'I','Í'=>'I','Ì'=>'I',
			'И'=>'I','Ǐ'=>'I','Ĩ'=>'I','Î'=>'I','IJ'=>'IJ','Ĵ'=>'J',
			'Й'=>'J','Я'=>'Ja','Ю'=>'Ju','К'=>'K','Ķ'=>'K','Ĺ'=>'L',
			'Л'=>'L','Ł'=>'L','Ŀ'=>'L','Ļ'=>'L','Ľ'=>'L','М'=>'M',
			'Н'=>'N','Ń'=>'N','Ñ'=>'N','Ņ'=>'N','Ň'=>'N','Ō'=>'O',
			'О'=>'O','Ǿ'=>'O','Ǒ'=>'O','Ơ'=>'O','Ŏ'=>'O','Ő'=>'O',
			'Ø'=>'O','Ö'=>'Oe','Õ'=>'O','Ó'=>'O','Ò'=>'O','Ô'=>'O',
			'Œ'=>'OE','П'=>'P','Ŗ'=>'R','Р'=>'R','Ř'=>'R','Ŕ'=>'R',
			'Ŝ'=>'S','Ş'=>'S','Š'=>'S','Ș'=>'S','Ś'=>'S','С'=>'S',
			'Ш'=>'Sh','Щ'=>'Shch','Ť'=>'T','Ŧ'=>'T','Ţ'=>'T','Ț'=>'T',
			'Т'=>'T','Ů'=>'U','Ű'=>'U','Ŭ'=>'U','Ũ'=>'U','Ų'=>'U',
			'Ū'=>'U','Ǜ'=>'U','Ǚ'=>'U','Ù'=>'U','Ú'=>'U','Ü'=>'Ue',
			'Ǘ'=>'U','Ǖ'=>'U','У'=>'U','Ư'=>'U','Ǔ'=>'U','Û'=>'U',
			'В'=>'V','Ŵ'=>'W','Ы'=>'Y','Ŷ'=>'Y','Ý'=>'Y','Ÿ'=>'Y',
			'Ź'=>'Z','З'=>'Z','Ż'=>'Z','Ž'=>'Z','Ж'=>'Zh','á'=>'a',
			'ă'=>'a','â'=>'a','à'=>'a','ā'=>'a','ǻ'=>'a','å'=>'a',
			'ä'=>'ae','ą'=>'a','ǎ'=>'a','ã'=>'a','а'=>'a','ª'=>'a',
			'æ'=>'ae','ǽ'=>'ae','б'=>'b','č'=>'c','ç'=>'c','ц'=>'c',
			'ċ'=>'c','ĉ'=>'c','ć'=>'c','ч'=>'ch','ð'=>'dj','ď'=>'dj',
			'д'=>'dj','đ'=>'dj','э'=>'e','é'=>'e','ё'=>'e','ë'=>'e',
			'ê'=>'e','е'=>'e','ĕ'=>'e','è'=>'e','ę'=>'e','ě'=>'e',
			'ė'=>'e','ē'=>'e','ƒ'=>'f','ф'=>'f','ġ'=>'g','ĝ'=>'g',
			'ğ'=>'g','г'=>'g','ģ'=>'g','х'=>'h','ĥ'=>'h','ħ'=>'h',
			'ǐ'=>'i','ĭ'=>'i','и'=>'i','ī'=>'i','ĩ'=>'i','į'=>'i',
			'ı'=>'i','ì'=>'i','î'=>'i','í'=>'i','ï'=>'i','ij'=>'ij',
			'ĵ'=>'j','й'=>'j','я'=>'ja','ю'=>'ju','ķ'=>'k','к'=>'k',
			'ľ'=>'l','ł'=>'l','ŀ'=>'l','ĺ'=>'l','ļ'=>'l','л'=>'l',
			'м'=>'m','ņ'=>'n','ñ'=>'n','ń'=>'n','н'=>'n','ň'=>'n',
			'ʼn'=>'n','ó'=>'o','ò'=>'o','ǒ'=>'o','ő'=>'o','о'=>'o',
			'ō'=>'o','º'=>'o','ơ'=>'o','ŏ'=>'o','ô'=>'o','ö'=>'oe',
			'õ'=>'o','ø'=>'o','ǿ'=>'o','œ'=>'oe','п'=>'p','р'=>'r',
			'ř'=>'r','ŕ'=>'r','ŗ'=>'r','ſ'=>'s','ŝ'=>'s','ș'=>'s',
			'š'=>'s','ś'=>'s','с'=>'s','ş'=>'s','ш'=>'sh','щ'=>'shch',
			'ß'=>'ss','ţ'=>'t','т'=>'t','ŧ'=>'t','ť'=>'t','ț'=>'t',
			'у'=>'u','ǘ'=>'u','ŭ'=>'u','û'=>'u','ú'=>'u','ų'=>'u',
			'ù'=>'u','ű'=>'u','ů'=>'u','ư'=>'u','ū'=>'u','ǚ'=>'u',
			'ǜ'=>'u','ǔ'=>'u','ǖ'=>'u','ũ'=>'u','ü'=>'ue','в'=>'v',
			'ŵ'=>'w','ы'=>'y','ÿ'=>'y','ý'=>'y','ŷ'=>'y','ź'=>'z',
			'ž'=>'z','з'=>'z','ż'=>'z','ж'=>'zh','ь'=>'','ъ'=>'',
			'\''=>'',
		];
	}

	/**
	*	Return a URL/filesystem-friendly version of string
	*	@return string
	*	@param $text string
	**/
	function slug($text) {
		return trim(strtolower(preg_replace('/([^\pL\pN])+/u','-',
			trim(strtr($text,Base::instance()->DIACRITICS+$this->diacritics())))),'-');
	}

	/**
	*	Return chunk of text from standard Lorem Ipsum passage
	*	@return string
	*	@param $count int
	*	@param $max int
	*	@param $std bool
	**/
	function filler($count=1,$max=20,$std=TRUE) {
		$out='';
		if ($std)
			$out='Lorem ipsum dolor sit amet, consectetur adipisicing elit, '.
				'sed do eiusmod tempor incididunt ut labore et dolore magna '.
				'aliqua.';
		$rnd=explode(' ',
			'a ab ad accusamus adipisci alias aliquam amet animi aperiam '.
			'architecto asperiores aspernatur assumenda at atque aut beatae '.
			'blanditiis cillum commodi consequatur corporis corrupti culpa '.
			'cum cupiditate debitis delectus deleniti deserunt dicta '.
			'dignissimos distinctio dolor ducimus duis ea eaque earum eius '.
			'eligendi enim eos error esse est eum eveniet ex excepteur '.
			'exercitationem expedita explicabo facere facilis fugiat harum '.
			'hic id illum impedit in incidunt ipsa iste itaque iure iusto '.
			'laborum laudantium libero magnam maiores maxime minim minus '.
			'modi molestiae mollitia nam natus necessitatibus nemo neque '.
			'nesciunt nihil nisi nobis non nostrum nulla numquam occaecati '.
			'odio officia omnis optio pariatur perferendis perspiciatis '.
			'placeat porro possimus praesentium proident quae quia quibus '.
			'quo ratione recusandae reiciendis rem repellat reprehenderit '.
			'repudiandae rerum saepe sapiente sequi similique sint soluta '.
			'suscipit tempora tenetur totam ut ullam unde vel veniam vero '.
			'vitae voluptas');
		for ($i=0,$add=$count-(int)$std;$i<$add;$i++) {
			shuffle($rnd);
			$words=array_slice($rnd,0,mt_rand(3,$max));
			$out.=' '.ucfirst(implode(' ',$words)).'.';
		}
		return $out;
	}

}

if (!function_exists('gzdecode')) {

	/**
	*	Decode gzip-compressed string
	*	@param $str string
	**/
	function gzdecode($str) {
		$fw=Base::instance();
		if (!is_dir($tmp=$fw->TEMP))
			mkdir($tmp,Base::MODE,TRUE);
		file_put_contents($file=$tmp.'/'.$fw->SEED.'.'.
			$fw->hash(uniqid(NULL,TRUE)).'.gz',$str,LOCK_EX);
		ob_start();
		readgzfile($file);
		$out=ob_get_clean();
		@unlink($file);
		return $out;
	}

}
diff --git a/vendor/fatfree-3.6/lib/web/geo.php b/vendor/fatfree-3.6/lib/web/geo.php
new file mode 100755
index 0000000..9d68002
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/web/geo.php
@@ -0,0 +1,108 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace Web;

//! Geo plug-in
class Geo extends \Prefab {

	/**
	*	Return information about specified Unix time zone
	*	@return array
	*	@param $zone string
	**/
	function tzinfo($zone) {
		$ref=new \DateTimeZone($zone);
		$loc=$ref->getLocation();
		$trn=$ref->getTransitions($now=time(),$now);
		$out=[
			'offset'=>$ref->
				getOffset(new \DateTime('now',new \DateTimeZone('UTC')))/3600,
			'country'=>$loc['country_code'],
			'latitude'=>$loc['latitude'],
			'longitude'=>$loc['longitude'],
			'dst'=>$trn[0]['isdst']
		];
		unset($ref);
		return $out;
	}

	/**
	*	Return geolocation data based on specified/auto-detected IP address
	*	@return array|FALSE
	*	@param $ip string
	**/
	function location($ip=NULL) {
		$fw=\Base::instance();
		$web=\Web::instance();
		if (!$ip)
			$ip=$fw->IP;
		$public=filter_var($ip,FILTER_VALIDATE_IP,
			FILTER_FLAG_IPV4|FILTER_FLAG_IPV6|
			FILTER_FLAG_NO_RES_RANGE|FILTER_FLAG_NO_PRIV_RANGE);
		if (function_exists('geoip_db_avail') &&
			geoip_db_avail(GEOIP_CITY_EDITION_REV1) &&
			$out=@geoip_record_by_name($ip)) {
			$out['request']=$ip;
			$out['region_code']=$out['region'];
			$out['region_name']=(!empty($out['country_code']) && !empty($out['region']))
				? geoip_region_name_by_code($out['country_code'],$out['region']) : '';
			unset($out['country_code3'],$out['region'],$out['postal_code']);
			return $out;
		}
		if (($req=$web->request('http://www.geoplugin.net/json.gp'.
			($public?('?ip='.$ip):''))) &&
			$data=json_decode($req['body'],TRUE)) {
			$out=[];
			foreach ($data as $key=>$val)
				if (!strpos($key,'currency') && $key!=='geoplugin_status'
					&& $key!=='geoplugin_region')
					$out[$fw->snakecase(substr($key, 10))]=$val;
			return $out;
		}
		return FALSE;
	}

	/**
	*	Return weather data based on specified latitude/longitude
	*	@return array|FALSE
	*	@param $latitude float
	*	@param $longitude float
	*	@param $key string
	**/
	function weather($latitude,$longitude,$key) {
		$fw=\Base::instance();
		$web=\Web::instance();
		$query=[
			'lat'=>$latitude,
			'lon'=>$longitude,
			'APPID'=>$key,
			'units'=>'metric'
		];
		return ($req=$web->request(
			'http://api.openweathermap.org/data/2.5/weather?'.
				http_build_query($query)))?
			json_decode($req['body'],TRUE):
			FALSE;
	}

}
diff --git a/vendor/fatfree-3.6/lib/web/google/recaptcha.php b/vendor/fatfree-3.6/lib/web/google/recaptcha.php
new file mode 100755
index 0000000..9c32979
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/web/google/recaptcha.php
@@ -0,0 +1,58 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace Web\Google;

//! Google ReCAPTCHA v2 plug-in
class Recaptcha {

	const
		//! API URL
		URL_Recaptcha='https://www.google.com/recaptcha/api/siteverify';

	/**
	 *	Verify reCAPTCHA response
	 *	@param string $secret
	 *	@param string $response
	 *	@return bool
	 **/
	static function verify($secret,$response=NULL) {
		$fw=\Base::instance();
		if (!isset($response))
			$response=$fw->{'POST.g-recaptcha-response'};
		if (!$response)
			return FALSE;
		$web=\Web::instance();
		$out=$web->request(self::URL_Recaptcha,[
			'method'=>'POST',
			'content'=>http_build_query([
				'secret'=>$secret,
				'response'=>$response,
				'remoteip'=>$fw->IP
			]),
		]);
		return isset($out['body']) &&
			($json=json_decode($out['body'],TRUE)) &&
			isset($json['success']) && $json['success'];
	}

}
diff --git a/vendor/fatfree-3.6/lib/web/google/staticmap.php b/vendor/fatfree-3.6/lib/web/google/staticmap.php
new file mode 100755
index 0000000..8310fe4
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/web/google/staticmap.php
@@ -0,0 +1,65 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace Web\Google;

//! Google Static Maps API v2 plug-in
class StaticMap {

	const
		//! API URL
		URL_Static='http://maps.googleapis.com/maps/api/staticmap';

	protected
		//! Query arguments
		$query=array();

	/**
	*	Specify API key-value pair via magic call
	*	@return object
	*	@param $func string
	*	@param $args array
	**/
	function __call($func,array $args) {
		$this->query[]=array($func,$args[0]);
		return $this;
	}

	/**
	*	Generate map
	*	@return string
	**/
	function dump() {
		$fw=\Base::instance();
		$web=\Web::instance();
		$out='';
		return ($req=$web->request(
			self::URL_Static.'?'.array_reduce(
				$this->query,
				function($out,$item) {
					return ($out.=($out?'&':'').
						urlencode($item[0]).'='.urlencode($item[1]));
				}
			))) && $req['body']?$req['body']:FALSE;
	}

}
diff --git a/vendor/fatfree-3.6/lib/web/oauth2.php b/vendor/fatfree-3.6/lib/web/oauth2.php
new file mode 100755
index 0000000..916674f
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/web/oauth2.php
@@ -0,0 +1,136 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace Web;

//! Lightweight OAuth2 client
class OAuth2 extends \Magic {

	protected
		//! Scopes and claims
		$args=[];

	/**
	*	Return OAuth2 authentication URI
	*	@return string
	*	@param $endpoint string
	**/
	function uri($endpoint) {
		return $endpoint.'?'.http_build_query($this->args);
	}

	/**
	*	Send request to API/token endpoint
	*	@return string|FALSE
	*	@param $uri string
	*	@param $method string
	*	@param $token array
	**/
	function request($uri,$method,$token=NULL) {
		$web=\Web::instance();
		$options=[
			'method'=>$method,
			'content'=>http_build_query($this->args),
			'header'=>['Accept: application/json']
		];
		if ($token)
			array_push($options['header'],'Authorization: Bearer '.$token);
		elseif ($method=='POST')
			array_push($options['header'],'Authorization: Basic '.
				base64_encode(
					$this->args['client_id'].':'.
					$this->args['client_secret']
				)
			);
		$response=$web->request($uri,$options);
		if ($response['error'])
			user_error($response['error'],E_USER_ERROR);
		return $response['body'] &&
			preg_grep('/HTTP\/1\.\d 200/',$response['headers'])?
			json_decode($response['body'],TRUE):
			FALSE;
	}

	/**
	*	Parse JSON Web token
	*	@return array
	*	@param $token string
	**/
	function jwt($token) {
		return json_decode(
			base64_decode(
				str_replace(
					['-','_'],
					['+','/'],
					explode('.',$token)[1]
				)
			),
			TRUE
		);
	}

	/**
	*	Return TRUE if scope/claim exists
	*	@return bool
	*	@param $key string
	**/
	function exists($key) {
		return isset($this->args[$key]);
	}

	/**
	*	Bind value to scope/claim
	*	@return string
	*	@param $key string
	*	@param $val string
	**/
	function set($key,$val) {
		return $this->args[$key]=$val;
	}

	/**
	*	Return value of scope/claim
	*	@return mixed
	*	@param $key string
	**/
	function &get($key) {
		if (isset($this->args[$key]))
			$val=&$this->args[$key];
		else
			$val=NULL;
		return $val;
	}

	/**
	*	Remove scope/claim
	*	@return NULL
	*	@param $key string
	**/
	function clear($key=NULL) {
		if ($key)
			unset($this->args[$key]);
		else
			$this->args=[];
	}

}

diff --git a/vendor/fatfree-3.6/lib/web/openid.php b/vendor/fatfree-3.6/lib/web/openid.php
new file mode 100755
index 0000000..435d89c
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/web/openid.php
@@ -0,0 +1,248 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace Web;

//! OpenID consumer
class OpenID extends \Magic {

	protected
		//! OpenID provider endpoint URL
		$url,
		//! HTTP request parameters
		$args=[];

	/**
	*	Determine OpenID provider
	*	@return string|FALSE
	*	@param $proxy string
	**/
	protected function discover($proxy) {
		// Normalize
		if (!preg_match('/https?:\/\//i',$this->args['endpoint']))
			$this->args['endpoint']='http://'.$this->args['endpoint'];
		$url=parse_url($this->args['endpoint']);
		// Remove fragment; reconnect parts
		$this->args['endpoint']=$url['scheme'].'://'.
			(isset($url['user'])?
				($url['user'].
				(isset($url['pass'])?(':'.$url['pass']):'').'@'):'').
			strtolower($url['host']).(isset($url['path'])?$url['path']:'/').
			(isset($url['query'])?('?'.$url['query']):'');
		// HTML-based discovery of OpenID provider
		$req=\Web::instance()->
			request($this->args['endpoint'],['proxy'=>$proxy]);
		if (!$req)
			return FALSE;
		$type=array_values(preg_grep('/Content-Type:/',$req['headers']));
		if ($type &&
			preg_match('/application\/xrds\+xml|text\/xml/',$type[0]) &&
			($sxml=simplexml_load_string($req['body'])) &&
			($xrds=json_decode(json_encode($sxml),TRUE)) &&
			isset($xrds['XRD'])) {
			// XRDS document
			$svc=$xrds['XRD']['Service'];
			if (isset($svc[0]))
				$svc=$svc[0];
			$svc_type=is_array($svc['Type'])?$svc['Type']:array($svc['Type']);
			if (preg_grep('/http:\/\/specs\.openid\.net\/auth\/2.0\/'.
					'(?:server|signon)/',$svc_type)) {
				$this->args['provider']=$svc['URI'];
				if (isset($svc['LocalID']))
					$this->args['localidentity']=$svc['LocalID'];
				elseif (isset($svc['CanonicalID']))
					$this->args['localidentity']=$svc['CanonicalID'];
			}
			$this->args['server']=$svc['URI'];
			if (isset($svc['Delegate']))
				$this->args['delegate']=$svc['Delegate'];
		}
		else {
			$len=strlen($req['body']);
			$ptr=0;
			// Parse document
			while ($ptr<$len)
				if (preg_match(
					'/^<link\b((?:\h+\w+\h*=\h*'.
					'(?:"(?:.+?)"|\'(?:.+?)\'))*)\h*\/?>/is',
					substr($req['body'],$ptr),$parts)) {
					if ($parts[1] &&
						// Process attributes
						preg_match_all('/\b(rel|href)\h*=\h*'.
							'(?:"(.+?)"|\'(.+?)\')/s',$parts[1],$attr,
							PREG_SET_ORDER)) {
						$node=[];
						foreach ($attr as $kv)
							$node[$kv[1]]=isset($kv[2])?$kv[2]:$kv[3];
						if (isset($node['rel']) &&
							preg_match('/openid2?\.(\w+)/',
								$node['rel'],$var) &&
							isset($node['href']))
							$this->args[$var[1]]=$node['href'];

					}
					$ptr+=strlen($parts[0]);
				}
				else
					$ptr++;
		}
		// Get OpenID provider's endpoint URL
		if (isset($this->args['provider'])) {
			// OpenID 2.0
			$this->args['ns']='http://specs.openid.net/auth/2.0';
			if (isset($this->args['localidentity']))
				$this->args['identity']=$this->args['localidentity'];
			if (isset($this->args['trust_root']))
				$this->args['realm']=$this->args['trust_root'];
		}
		elseif (isset($this->args['server'])) {
			// OpenID 1.1
			$this->args['ns']='http://openid.net/signon/1.1';
			if (isset($this->args['delegate']))
				$this->args['identity']=$this->args['delegate'];
		}
		if (isset($this->args['provider'])) {
			// OpenID 2.0
			if (empty($this->args['claimed_id']))
				$this->args['claimed_id']=$this->args['identity'];
			return $this->args['provider'];
		}
		elseif (isset($this->args['server']))
			// OpenID 1.1
			return $this->args['server'];
		else
			return FALSE;
	}

	/**
	*	Initiate OpenID authentication sequence; Return FALSE on failure
	*	or redirect to OpenID provider URL
	*	@return bool
	*	@param $proxy string
	*	@param $attr array
	*	@param $reqd string|array
	**/
	function auth($proxy=NULL,$attr=[],array $reqd=NULL) {
		$fw=\Base::instance();
		$root=$fw->SCHEME.'://'.$fw->HOST;
		if (empty($this->args['trust_root']))
			$this->args['trust_root']=$root.$fw->BASE.'/';
		if (empty($this->args['return_to']))
			$this->args['return_to']=$root.$_SERVER['REQUEST_URI'];
		$this->args['mode']='checkid_setup';
		if ($this->url=$this->discover($proxy)) {
			if ($attr) {
				$this->args['ns.ax']='http://openid.net/srv/ax/1.0';
				$this->args['ax.mode']='fetch_request';
				foreach ($attr as $key=>$val)
					$this->args['ax.type.'.$key]=$val;
				$this->args['ax.required']=is_string($reqd)?
					$reqd:implode(',',$reqd);
			}
			$var=[];
			foreach ($this->args as $key=>$val)
				$var['openid.'.$key]=$val;
			$fw->reroute($this->url.'?'.http_build_query($var));
		}
		return FALSE;
	}

	/**
	*	Return TRUE if OpenID verification was successful
	*	@return bool
	*	@param $proxy string
	**/
	function verified($proxy=NULL) {
		preg_match_all('/(?<=^|&)openid\.([^=]+)=([^&]+)/',
			$_SERVER['QUERY_STRING'],$matches,PREG_SET_ORDER);
		foreach ($matches as $match)
			$this->args[$match[1]]=urldecode($match[2]);
		if (isset($this->args['mode']) &&
			$this->args['mode']!='error' &&
			$this->url=$this->discover($proxy)) {
			$this->args['mode']='check_authentication';
			$var=[];
			foreach ($this->args as $key=>$val)
				$var['openid.'.$key]=$val;
			$req=\Web::instance()->request(
				$this->url,
				[
					'method'=>'POST',
					'content'=>http_build_query($var),
					'proxy'=>$proxy
				]
			);
			return (bool)preg_match('/is_valid:true/i',$req['body']);
		}
		return FALSE;
	}

	/**
	*	Return OpenID response fields
	*	@return array
	**/
	function response() {
		return $this->args;
	}

	/**
	*	Return TRUE if OpenID request parameter exists
	*	@return bool
	*	@param $key string
	**/
	function exists($key) {
		return isset($this->args[$key]);
	}

	/**
	*	Bind value to OpenID request parameter
	*	@return string
	*	@param $key string
	*	@param $val string
	**/
	function set($key,$val) {
		return $this->args[$key]=$val;
	}

	/**
	*	Return value of OpenID request parameter
	*	@return mixed
	*	@param $key string
	**/
	function &get($key) {
		if (isset($this->args[$key]))
			$val=&$this->args[$key];
		else
			$val=NULL;
		return $val;
	}

	/**
	*	Remove OpenID request parameter
	*	@return NULL
	*	@param $key
	**/
	function clear($key) {
		unset($this->args[$key]);
	}

}
diff --git a/vendor/fatfree-3.6/lib/web/pingback.php b/vendor/fatfree-3.6/lib/web/pingback.php
new file mode 100755
index 0000000..a68430c
--- /dev/null
+++ b/vendor/fatfree-3.6/lib/web/pingback.php
@@ -0,0 +1,176 @@
<?php

/*

	Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.

	This file is part of the Fat-Free Framework (http://fatfreeframework.com).

	This is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or later.

	Fat-Free Framework is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>.

*/

namespace Web;

//! Pingback 1.0 protocol (client and server) implementation
class Pingback extends \Prefab {

	protected
		//! Transaction history
		$log;

	/**
	*	Return TRUE if URL points to a pingback-enabled resource
	*	@return bool
	*	@param $url
	**/
	protected function enabled($url) {
		$web=\Web::instance();
		$req=$web->request($url);
		$found=FALSE;
		if ($req['body']) {
			// Look for pingback header
			foreach ($req['headers'] as $header)
				if (preg_match('/^X-Pingback:\h*(.+)/',$header,$href)) {
					$found=$href[1];
					break;
				}
			if (!$found &&
				// Scan page for pingback link tag
				preg_match('/<link\h+(.+?)\h*\/?>/i',$req['body'],$parts) &&
				preg_match('/rel\h*=\h*"pingback"/i',$parts[1]) &&
				preg_match('/href\h*=\h*"\h*(.+?)\h*"/i',$parts[1],$href))
				$found=$href[1];
		}
		return $found;
	}

	/**
	*	Load local page contents, parse HTML anchor tags, find permalinks,
	*	and send XML-RPC calls to corresponding pingback servers
	*	@return NULL
	*	@param $source string
	**/
	function inspect($source) {
		$fw=\Base::instance();
		$web=\Web::instance();
		$parts=parse_url($source);
		if (empty($parts['scheme']) || empty($parts['host']) ||
			$parts['host']==$fw->HOST) {
			$req=$web->request($source);
			$doc=new \DOMDocument('1.0',$fw->ENCODING);
			$doc->stricterrorchecking=FALSE;
			$doc->recover=TRUE;
			if (@$doc->loadhtml($req['body'])) {
				// Parse anchor tags
				$links=$doc->getelementsbytagname('a');
				foreach ($links as $link) {
					$permalink=$link->getattribute('href');
					// Find pingback-enabled resources
					if ($permalink && $found=$this->enabled($permalink)) {
						$req=$web->request($found,
							[
								'method'=>'POST',
								'header'=>'Content-Type: application/xml',
								'content'=>xmlrpc_encode_request(
									'pingback.ping',
									[$source,$permalink],
									['encoding'=>$fw->ENCODING]
								)
							]
						);
						if ($req['body'])
							$this->log.=date('r').' '.
								$permalink.' [permalink:'.$found.']'.PHP_EOL.
								$req['body'].PHP_EOL;
					}
				}
			}
			unset($doc);
		}
	}

	/**
	*	Receive ping, check if local page is pingback-enabled, verify
	*	source contents, and return XML-RPC response
	*	@return string
	*	@param $func callback
	*	@param $path string
	**/
	function listen($func,$path=NULL) {
		$fw=\Base::instance();
		if (PHP_SAPI!='cli') {
			header('X-Powered-By: '.$fw->PACKAGE);
			header('Content-Type: application/xml; '.
				'charset='.$charset=$fw->ENCODING);
		}
		if (!$path)
			$path=$fw->BASE;
		$web=\Web::instance();
		$args=xmlrpc_decode_request($fw->BODY,$method,$charset);
		$options=['encoding'=>$charset];
		if ($method=='pingback.ping' && isset($args[0],$args[1])) {
			list($source,$permalink)=$args;
			$doc=new \DOMDocument('1.0',$fw->ENCODING);
			// Check local page if pingback-enabled
			$parts=parse_url($permalink);
			if ((empty($parts['scheme']) ||
				$parts['host']==$fw->HOST) &&
				preg_match('/^'.preg_quote($path,'/').'/'.
					($fw->CASELESS?'i':''),$parts['path']) &&
				$this->enabled($permalink)) {
				// Check source
				$parts=parse_url($source);
				if ((empty($parts['scheme']) ||
					$parts['host']==$fw->HOST) &&
					($req=$web->request($source)) &&
					$doc->loadhtml($req['body'])) {
					$links=$doc->getelementsbytagname('a');
					foreach ($links as $link) {
						if ($link->getattribute('href')==$permalink) {
							call_user_func_array($func,[$source,$req['body']]);
							// Success
							die(xmlrpc_encode_request(NULL,$source,$options));
						}
					}
					// No link to local page
					die(xmlrpc_encode_request(NULL,0x11,$options));
				}
				// Source failure
				die(xmlrpc_encode_request(NULL,0x10,$options));
			}
			// Doesn't exist (or not pingback-enabled)
			die(xmlrpc_encode_request(NULL,0x21,$options));
		}
		// Access denied
		die(xmlrpc_encode_request(NULL,0x31,$options));
	}

	/**
	*	Return transaction history
	*	@return string
	**/
	function log() {
		return $this->log;
	}

	/**
	*	Instantiate class
	*	@return object
	**/
	function __construct() {
		// Suppress errors caused by invalid HTML structures
		libxml_use_internal_errors(TRUE);
	}

}
diff --git a/vendor/fatfree-3.6/readme.md b/vendor/fatfree-3.6/readme.md
new file mode 100755
index 0000000..098fb04
--- /dev/null
+++ b/vendor/fatfree-3.6/readme.md
@@ -0,0 +1,2599 @@
[![Fat-Free Framework]ui/images/logo.png]http://fatfree.sf.net/

**A powerful yet easy-to-use PHP micro-framework designed to help you build dynamic and robust Web applications - fast!**

[![Flattr this project]https://api.flattr.com/button/flattr-badge-large.png]https://flattr.com/submit/auto?user_id=phpfatfree&url=https://github.com/bcosca/fatfree

Condensed in a single ~65KB file, F3 (as we fondly call it) gives you solid foundation, a mature code base, and a no-nonsense approach to writing Web applications. Under the hood is an easy-to-use Web development tool kit, a high-performance URL routing and cache engine, built-in code highlighting, and support for multilingual applications. It's lightweight, easy-to-use, and fast. Most of all, it doesn't get in your way.

Whether you're a novice or an expert PHP programmer, F3 will get you up and running in no time. No unnecessary and painstaking installation procedures. No complex configuration required. No convoluted directory structures. There's no better time to start developing Web applications the easy way than right now!

F3 supports both SQL and NoSQL databases off-the-shelf: MySQL, SQLite, MSSQL/Sybase, PostgreSQL, DB2, and MongoDB. It also comes with powerful object-relational mappers for data abstraction and modeling that are just as lightweight as the framework. No configuration needed.

That's not all. F3 is packaged with other optional plug-ins that extend its capabilities:-

* Fast and clean template engine,
* Unit testing toolkit,
* Database-managed sessions with automatic CSRF protection,
* Markdown-to-HTML converter,
* Atom/RSS feed reader,
* Image processor,
* Geodata handler,
* Google static maps,
* On-the-fly Javascript/CSS compressor,
* OpenID (consumer),
* Custom logger,
* Basket/Shopping cart,
* Pingback server/consumer,
* Unicode-aware string functions,
* SMTP over SSL/TLS,
* Tools for communicating with other servers,
* And more in a tiny supercharged package!

Unlike other frameworks, F3 aims to be usable - not usual.

[![Flattr this project]https://api.flattr.com/button/flattr-badge-large.png]https://flattr.com/submit/auto?user_id=phpfatfree&url=https://github.com/bcosca/fatfree

The philosophy behind the framework and its approach to software architecture is towards minimalism in structural components, avoiding application complexity and striking a balance between code elegance, application performance and programmer productivity.

[![Paypal]ui/images/paypal.png]https://www.paypal.me/fatfree

![Bitcoin]ui/images/bitcoin.png

## Table of Contents

* [Getting Started]#getting-started
* [Routing Engine]#routing-engine
* [Framework Variables]#framework-variables
* [Views and Templates]#views-and-templates
* [Databases]#databases
* [Plug-Ins]#plug-ins
* [Optimization]#optimization
* [Unit Testing]#unit-testing
* [Quick Reference]#quick-reference
* [Support and Licensing]#support-and-licensing

[![Twitter]ui/images/twitter.png]https://twitter.com/phpfatfree

### Version 3.6 Is Finally Released!

The latest official release welcomes the new year with a bang and marks the final milestone in this version of the Fat-Free Framework. Packed with exciting new features and outstanding documentation that consumed significant time and effort to develop and refine, version 3.6 is now available for download. This edition is packed with a bunch of new usability and security features.

F3 has a stable enterprise-class architecture. Unbeatable performance, user-friendly features and a lightweight footprint. What more can you ask for?

It is highly recommended that experienced users develop new applications with this version to take advantage of the latest code base and its significant improvements.

## Introducing FatFreeFramework.com

**Detailed API documentation with lots of code examples and a graphic guide can now be found at [http://fatfreeframework.com/]http://fatfreeframework.com/.**

Of course this handy online reference is powered by F3! It showcases the framework's capability and performance. Check it out now.

## Getting Started

> *A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away. -- Antoine de Saint-Exupéry*

Fat-Free Framework makes it easy to build entire Web sites in a jiffy. With the same power and brevity as modern Javascript toolkits and libraries, F3 helps you write better-looking and more reliable PHP programs. One glance at your PHP source code and anyone will find it easy to understand, how much you can accomplish in so few lines of code, and how powerful the results are.

F3 is one of the best documented frameworks around. Learning it costs next to nothing. No strict set of difficult-to-navigate directory structures and obtrusive programming steps. No truck load of configuration options just to display `'Hello, World'` in your browser. Fat-Free gives you a lot of freedom - and style - to get more work done with ease and in less time.

F3's declarative approach to programming makes it easy for novices and experts alike to understand PHP code. If you're familiar with the programming language Ruby, you'll notice the resemblance between Fat-Free and Sinatra micro-framework because they both employ a simple Domain-Specific Language for ReSTful Web services. But unlike Sinatra and its PHP incarnations (Fitzgerald, Limonade, Glue - to name a few), Fat-Free goes beyond just handling routes and requests. Views can be in any form, such as plain text, HTML, XML or an e-mail message. The framework comes with a fast and easy-to-use template engine. F3 also works seamlessly with other template engines, including Twig, Smarty, and PHP itself. Models communicate with F3's data mappers and the SQL helper for more complex interactions with various database engines. Other plug-ins extend the base functionality even more. It's a total Web development framework - with a lot of muscle!

### Enough Said - See For Yourself

Unzip the contents of the distribution package anywhere in your hard drive. By default, the framework file and optional plug-ins are located in the `lib/` path. Organize your directory structures any way you want. You may move the default folders to a path that's not Web-accessible for better security. Delete the plug-ins that you don't need. You can always restore them later and F3 will detect their presence automatically.

**Important:** If your application uses APC, Memcached, WinCache, XCache, or a filesystem cache, clear all cache entries first before overwriting an older version of the framework with a new one.

Make sure you're running the right version of PHP. F3 does not support versions earlier than PHP 5.4. You'll be getting syntax errors (false positives) all over the place because new language constructs and closures/anonymous functions are not supported by outdated PHP versions. To find out, open your console (`bash` shell on GNU/Linux, or `cmd.exe` on Windows):-

```
/path/to/php -v
```

PHP will let you know which particular version you're running and you should get something that looks similar to this:-

```
PHP 5.4.30 (cli) (built: Jul 22 2014 21:34:41)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
```

Upgrade if necessary and come back here if you've made the jump to PHP 5.4 or a later release. If you need a PHP 5.4+ hosting service provider, try one of these services:

* [A2 Hosting]http://www.a2hosting.com/2461-15-1-72.html
* [DreamHost]http://www.dreamhost.com/r.cgi?665472
* [Hostek]http://hostek.com/aff.php?aff=364&plat=L
* [SiteGround]http://www.siteground.com/index.htm?referrerid=155694

### Hello, World: The Less-Than-A-Minute Fat-Free Recipe

Time to start writing our first application:-

``` php
$f3 = require('path/to/base.php');
$f3->route('GET /',
    function() {
        echo 'Hello, world!';
    }
);
$f3->run();
```

Prepend `base.php` on the first line with the appropriate path. Save the above code fragment as `index.php` in your Web root folder. We've written our first Web page.

Using composer? Then just run `composer require bcosca/fatfree` and use the following:

``` php
require 'vendor/autoload.php';
$f3 = \Base::instance();
$f3->route('GET /',
    function() {
        echo 'Hello, world!';
    }
);
$f3->run();
```

The first command tells the PHP interpreter that you want the framework's functions and features available to your application. The `$f3->route()` method informs Fat-Free that a Web page is available at the relative URL indicated by the slash (`/`). Anyone visiting your site located at `http://www.example.com/` will see the `'Hello, world!'` message because the URL `/` is equivalent to the root page. To create a route that branches out from the root page, like `http://www.example.com/inside/`, you can define another route with a simple `GET /inside` string.

The route described above tells the framework to render the page only when it receives a URL request using the HTTP `GET` method. More complex Web sites containing forms use other HTTP methods like `POST`, and you can also implement that as part of a `$f3->route()` specification.

If the framework sees an incoming request for your Web page located at the root URL `/`, it will automatically route the request to the callback function, which contains the code necessary to process the request and render the appropriate HTML stuff. In this example, we just send the string `'Hello, world!'` to the user's Web browser.

So we've established our first route. But that won't do much, except to let F3 know that there's a process that will handle it and there's some text to display on the user's Web browser. If you have a lot more pages on your site, you need to set up different routes for each group. For now, let's keep it simple. To instruct the framework to start waiting for requests, we issue the `$f3->run()` command.

**Can't Get the Example Running?** If you're having trouble getting this simple program to run on your server, you may have to tweak your Web server settings a bit. Take a look at the sample Apache configuration in the following section (along with the Nginx and Lighttpd equivalents).

**Still having trouble?** Make sure the `$f3 = require('path/to/base.php');` assignment comes before any output in your script. `base.php` modifies the HTTP headers, so any character that is output to the browser before this assignment will cause errors.

## Routing Engine

### Overview

Our first example wasn't too hard to swallow, was it? If you like a little more flavor in your Fat-Free soup, insert another route before the `$f3->run()` command:-

``` php
$f3->route('GET /about',
    function() {
        echo 'Donations go to a local charity... us!';
    }
);
```

You don't want to clutter the global namespace with function names? Fat-Free recognizes different ways of mapping route handlers to OOP classes and methods:-

``` php
class WebPage {
    function display() {
        echo 'I cannot object to an object';
    }
}

$f3->route('GET /about','WebPage->display');
```

HTTP requests can also be routed to static class methods:-

``` php
$f3->route('GET /login','Auth::login');
```

Passed arguments are always provided as the second parameter:

``` php
$f3->route('GET /hello/@name','User::greet');

class User {
	public static function greet($f3, $args) { //$args is type of Array
		echo "Hello " . $args['name'];
	}
}
```
If the provided name argument would be **foo** (/hello/foo), the following output would be shown:

```
Hello foo
```

### Routes and Tokens

As a demonstration of Fat-Free's powerful domain-specific language (DSL), you can specify a single route to handle different possibilities:-

``` php
$f3->route('GET /brew/@count',
    function($f3) {
        echo $f3->get('PARAMS.count').' bottles of beer on the wall.';
    }
);
```

This example shows how we can specify a token `@count` to represent part of a URL. The framework will serve any request URL that matches the `/brew/` prefix, like `/brew/99`, `/brew/98`, etc. This will display `'99 bottles of beer on the wall'` and `'98 bottles of beer on the wall'`, respectively. Fat-Free will also accept a page request for `/brew/unbreakable`. (Expect this to display `'unbreakable bottles of beer on the wall'`.) When such a dynamic route is specified, Fat-Free automagically populates the global `PARAMS` array variable with the value of the captured strings in the URL. The `$f3->get()` call inside the callback function retrieves the value of a framework variable. You can certainly apply this method in your code as part of the presentation or business logic. But we'll discuss that in greater detail later.

Notice that Fat-Free understands array dot-notation. You can use `PARAMS['count']` regular notation instead in code, which is prone to typo errors and unbalanced braces. In views and templates, the framework permits `@PARAMS.count` notation which is somewhat similar to Javascript. (We'll cover views and templates later.)

Here's another way to access tokens in a request pattern:-

``` php
$f3->route('GET /brew/@count',
    function($f3,$params) {
        echo $params['count'].' bottles of beer on the wall.';
    }
);
```

You can use the asterisk (`*`) to accept any URL after the `/brew` route - if you don't really care about the rest of the path:-

``` php
$f3->route('GET /brew/*',
    function() {
        echo 'Enough beer! We always end up here.';
    }
);
```

An important point to consider: You will get Fat-Free (and yourself) confused if you have both `GET /brew/@count` and `GET /brew/*` together in the same application. Use one or the other. Another thing: Fat-Free sees `GET /brew` as separate and distinct from the route `GET /brew/@count`. Each can have different route handlers.


### Dynamic Web Sites

Wait a second - in all the previous examples, we never really created any directory in our hard drive to store these routes. The short answer: we don't have to. All F3 routes are virtual. They don't mirror our hard disk folder structure. If you have programs or static files (images, CSS, etc.) that do not use the framework - as long as the paths to these files do not conflict with any route defined in your application - your Web server software will deliver them to the user's browser, provided the server is configured properly.

### Named Routes

When you define a route, you can assign it a name. Use the route name in your code and templates instead of a typed url. Then if you need to change your urls to please the marketing overlords, you only need to make the change where the route was defined. The route names must follow php variable naming rules (no dots, dashes nor hyphens).

Let's name a route:-

``` php
$f3->route('GET @beer_list: /beer', 'Beer->list');
```

The name is inserted after the route VERB (`GET` in this example) preceded by an `@` symbol, and separated from the URL portion by a colon `:` symbol. You can insert a space after the colon if that makes it easier to read your code (as shown here).

To access the named route in a template, get the value of the named route as the key of the `ALIASES` hive array:-

``` html
<a href="{{ @ALIASES.beer_list }}">View beer list</a>
```

To redirect the visitor to a new URL, call the named route inside the `reroute()` method like:-

``` php
// a named route is a string value
$f3->reroute('@beer_list'); // note the single quotes
```

If you use tokens in your route, F3 will replace those tokens with their current value. If you want to change the token's value before calling reroute, pass it as the 2nd argument.:-

``` php
$f3->route('GET @beer_list: /beer/@country', 'Beer->bycountry');
$f3->route('GET @beer_list: /beer/@country/@village', 'Beer->byvillage');

// a set of key-value pairs is passed as argument to named route
$f3->reroute('@beer_list(@country=Germany)');

// if more than one token needed
$f3->reroute('@beer_list(@country=Germany,@village=Rhine)');
```

Remember to `urlencode()` your arguments if you have characters that do not comply with RFC 1738 guidelines for well-formed URLs.

### PHP 5.4's Built-In Web Server

PHP's latest stable version has its own built-in Web server. Start it up using the following configuration:-

```
php -S localhost:80 -t /var/www/
```

The above command will start routing all requests to the Web root `/var/www`. If an incoming HTTP request for a file or folder is received, PHP will look for it inside the Web root and send it over to the browser if found. Otherwise, PHP will load the default `index.php` (containing your F3-enabled code).

### Sample Apache Configuration

If you're using Apache, make sure you activate the URL rewriting module (mod_rewrite) in your apache.conf (or httpd.conf) file. You should also create a .htaccess file containing the following:-

``` apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule .* index.php [L,QSA]
```

The script tells Apache that whenever an HTTP request arrives and if no physical file (`!-f`) or path (`!-d`) or symbolic link (`!-l`) can be found, it should transfer control to `index.php`, which contains our main/front controller, and which in turn, invokes the framework.

The `.htaccess file` containing the Apache directives stated above should always be in the same folder as `index.php`.

You also need to set up Apache so it knows the physical location of `index.php` in your hard drive. A typical configuration is:-

``` apache
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
    Options -Indexes +FollowSymLinks +Includes
    AllowOverride All
    Order allow,deny
    Allow from All
</Directory>
```

If you're developing several applications simultaneously, a virtual host configuration is easier to manage:-

``` apache
NameVirtualHost *
<VirtualHost *>
    ServerName site1.com
    DocumentRoot "/var/www/site1"
    <Directory "/var/www/site1">
        Options -Indexes +FollowSymLinks +Includes
        AllowOverride All
        Order allow,deny
        Allow from All
    </Directory>
</VirtualHost>
<VirtualHost *>
    ServerName site2.com
    DocumentRoot "/var/www/site2"
    <Directory "/var/www/site2">
        Options -Indexes +FollowSymLinks +Includes
        AllowOverride All
        Order allow,deny
        Allow from All
    </Directory>
</VirtualHost>
```

Each `ServerName` (`site1.com` and `site2.com` in our example) must be listed in your `/etc/hosts` file. On Windows, you should edit `C:/WINDOWS/system32/drivers/etc/hosts`. A reboot might be necessary to effect the changes. You can then point your Web browser to the address `http://site1.com` or `http://site2.com`. Virtual hosts make your applications a lot easier to deploy.

### Sample Nginx Configuration

For Nginx servers, here's the recommended configuration (replace ip_address:port with your environment's FastCGI PHP settings):-

``` nginx
server {
    root /var/www/html;
    location / {
        index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ {
        fastcgi_pass ip_address:port;
        include fastcgi_params;
    }
}
```

### Sample Lighttpd Configuration

Lighttpd servers are configured in a similar manner:-

```
$HTTP["host"] =~ "www\.example\.com$" {
    url.rewrite-once = ( "^/(.*?)(\?.+)?$"=>"/index.php/$1?$2" )
    server.error-handler-404 = "/index.php"
}
```

### Sample IIS Configuration

Install the [URL rewrite module]http://www.iis.net/downloads/microsoft/url-rewrite and the appropriate .NET framework corresponding to your Windows version. Then create a file named `web.config` in your application root with the following contents:

```
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Application" stopProcessing="true">
          <match url=".*" ignoreCase="false" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
          </conditions>
          <action type="Rewrite" url="index.php" appendQueryString="true" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
```

### Rerouting

So let's get back to coding. You can declare a page obsolete and redirect your visitors to another site/page:-

``` php
$f3->route('GET|HEAD /obsoletepage',
    function($f3) {
        $f3->reroute('/newpage');
    }
);
```

If someone tries to access the URL `http://www.example.com/obsoletepage` using either HTTP GET or HEAD request, the framework redirects the user to the URL: `http://www.example.com/newpage` as shown in the above example. You can also redirect the user to another site, like `$f3->reroute('http://www.anotherexample.org/');`.

Rerouting can be particularly useful when you need to do some maintenance work on your site. You can have a route handler that informs your visitors that your site is offline for a short period.

HTTP redirects are indispensable but they can also be expensive. As much as possible, refrain from using `$f3->reroute()` to send a user to another page on the same Web site if you can direct the flow of your application by invoking the function or method that handles the target route. However, this approach will not change the URL on the address bar of the user's Web browser. If this is not the behavior you want and you really need to send a user to another page, in instances like successful submission of a form or after a user has been authenticated, Fat-Free sends an `HTTP 303 See Other` header. For all other attempts to reroute to another page or site, the framework sends an `HTTP 301 Moved Permanently` header.

### Triggering a 404

At runtime, Fat-Free automatically generates an HTTP 404 error whenever it sees that an incoming HTTP request does not match any of the routes defined in your application. However, there are instances when you need to trigger it yourself.

Take for instance a route defined as `GET /dogs/@breed`. Your application logic may involve searching a database and attempting to retrieve the record corresponding to the value of `@breed` in the incoming HTTP request. Since Fat-Free will accept any value after the `/dogs/` prefix because of the presence of the `@breed` token, displaying an `HTTP 404 Not Found` message programmatically becomes necessary when the program doesn't find any match in our database. To do that, use the following command:-

``` php
$f3->error(404);
```

### Representational State Transfer (ReST)

Fat-Free's architecture is based on the concept that HTTP URIs represent abstract Web resources (not limited to HTML) and each resource can move from one application state to another. For this reason, F3 does not have any restrictions on the way you structure your application. If you prefer to use the [Model-View-Controller]http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller pattern, F3 can help you compartmentalize your application components to stick to this paradigm. On the other hand, the framework also supports the [Resource-Method-Representation]http://www.peej.co.uk/articles/rmr-architecture.html pattern, and implementing it is more straightforward.

Here's an example of a ReST interface:-

``` php
class Item {
    function get() {}
    function post() {}
    function put() {}
    function delete() {}
}

$f3=require('lib/base.php');
$f3->map('/cart/@item','Item');
$f3->run();
```

Fat-Free's `$f3->map()` method provides a ReST interface by mapping HTTP methods in routes to the equivalent methods of an object or a PHP class. If your application receives an incoming HTTP request like `GET /cart/123`, Fat-Free will automatically transfer control to the object's or class' `get()` method. On the other hand, a `POST /cart/123` request will be routed to the `Item` class' `post()` method.

**Note:** Browsers do not implement the HTTP `PUT` and `DELETE` methods in regular HTML forms. These and other ReST methods (`HEAD`, and `CONNECT`) are accessible only via AJAX calls to the server.

If the framework receives an HTTP request for a route that maps to a method that is not implemented by a class (perhaps you've made an error in the route mapping, or the method is not written yet), it generates an `HTTP 405 Method Not Allowed` error.

If a client requests HTTP `OPTIONS` for a URL resource, F3 responds with the appropriate HTTP headers that indicate which methods are allowed for the resource (HEAD, GET, PUT, etc). The framework will not map the `OPTIONS` request to a class.

### The F3 Autoloader

Fat-Free has a way of loading classes only at the time you need them, so they don't gobble up more memory than a particular segment of your application needs. And you don't have to write a long list of `include` or `require` statements just to load PHP classes saved in different files and different locations. The framework can do this automatically for you. Just save your files (one class per file) in a folder and tell the framework to automatically load the appropriate file once you invoke a method in the class:-

``` php
$f3->set('AUTOLOAD','autoload/');
```

You can assign a different location for your autoloaded classes by changing the value of the `AUTOLOAD` global variable. You can also have multiple autoload paths. If you have your classes organized and in different folders, you can instruct the framework to autoload the appropriate class when a static method is called or when an object is instantiated. Modify the `AUTOLOAD` variable this way:-

``` php
$f3->set('AUTOLOAD','admin/autoload/; user/autoload/; default/');
```

**Important:** Except for the .php extension, the class name and file name must be identical, for the framework to autoload your class properly. The basename of this file must be identical to your class invocation, e.g. F3 will look for either `Foo/BarBaz.php` or `foo/barbaz.php` when it detects a `new Foo\BarBaz` statement in your application.

### Working with Namespaces

`AUTOLOAD` allows class hierarchies to reside in similarly-named subfolders, so if you want the framework to autoload a PHP 5.4 namespaced class that's invoked in the following manner:-

``` php
$f3->set('AUTOLOAD','autoload/');
$obj=new Gadgets\iPad;
```

You can create a folder hierarchy that follows the same structure. Assuming `/var/www/html/` is your Web root, then F3 will look for the class in `/var/www/html/autoload/gadgets/ipad.php`. The file `ipad.php` should have the following minimum code:-

``` php
namespace Gadgets;
class iPad {}
```

Remember: All directory names in Fat-Free must end with a slash. You can assign a search path for the autoloader as follows:-

``` php
$f3->set('AUTOLOAD','main/;aux/');
```

### Routing to a Namespaced Class

F3, being a namespace-aware framework, allows you to use a method in namespaced class as a route handler, and there are several ways of doing it. To call a static method:-

``` php
$f3->set('AUTOLOAD','classes/');
$f3->route('GET|POST /','Main\Home::show');
```

The above code will invoke the static `show()` method of the class `Home` within the `Main` namespace. The `Home` class must be saved in the folder `classes/main/home.php` for it to be loaded automatically.

If you prefer to work with objects:-

``` php
$f3->route('GET|POST /','Main\Home->show');
```

will instantiate the `Home` class at runtime and call the `show()` method thereafter.

### Event Handlers

F3 has a couple of routing event listeners that might help you improve the flow and structure of controller classes. Say you have a route defined as follows:-

``` php
$f3->route('GET /','Main->home');
```

If the application receives an HTTP request matching the above route, F3 instantiates `Main`, but before executing the `home()` method, the framework looks for a method in this class named `beforeRoute()`. In case it's found, F3 runs the code contained in the `beforeRoute()` event handler before transferring control to the `home()` method. Once this is accomplished, the framework looks for an `afterRoute()` event handler. Like `beforeRoute()`, the method gets executed if it's defined.

### Dynamic Route Handlers

Here's another F3 goodie:-

``` php
$f3->route('GET /products/@action','Products->@action');
```

If your application receives a request for, say, `/products/itemize`, F3 will extract the `'itemize'` string from the URL and pass it on to the `@action` token in the route handler. F3 will then look for a class named `Products` and execute the `itemize()` method.

Dynamic route handlers may have various forms:-

``` php
// static method
$f3->route('GET /public/@genre','Main::@genre');
// object mode
$f3->route('GET /public/@controller/@action','@controller->@action');
```

F3 triggers an `HTTP 404 Not Found` error at runtime if it cannot transfer control to the class or method associated with the current route, i.e. an undefined class or method.

### AJAX and Synchronous Requests

Routing patterns may contain modifiers that direct the framework to base its routing decision on the type of HTTP request:-

``` php
$f3->route('GET /example [ajax]','Page->getFragment');
$f3->route('GET /example [sync]','Page->getFull');
```

The first statement will route the HTTP request to the `Page->getFragment()` callback only if an `X-Requested-With: XMLHttpRequest` header (AJAX object) is received by the server. If an ordinary (synchronous) request is detected, F3 will simply drop down to the next matching pattern, and in this case it executes the `Page->getFull()` callback.

If no modifiers are defined in a routing pattern, then both AJAX and synchronous request types are routed to the specified handler.

Route pattern modifiers are also recognized by `$f3->map()`.

## Framework Variables

### Basic Use

Variables defined in Fat-Free are global, i.e. they can be accessed by any MVC component. Framework globals are not identical to PHP globals. An F3 variable named `content` is not identical to PHP's `$content`. F3 is a domain-specific language in its own right and maintains its own separate symbol table for system and application variables. The framework, like every well-designed object-oriented program, does not pollute the PHP global namespace with constants, variables, functions or classes that might conflict with any application. Unlike other frameworks, F3 does not use PHP's `define()` statement. All framework constants are confined to classes.

To assign a value to a Fat-Free variable:

``` php
$f3->set('var',value); // or
$f3->var=value;

$f3->set('hello.world','good morning'); // translates to: 'hello' == array('world'=>'good morning')
$f3->{'hello.world'}='good morning'; // same as prior statement
```

**Note:** Fat-Free variables accept all PHP data types, including objects and anonymous functions.

To set several variables at once:

``` php
$f3->mset(
    [
        'foo'=>'bar',
        'baz'=>123
    ]
);
```

To retrieve the value of a framework variable named `var`:-

``` php
echo $f3->get('var'); // or
echo $f3->var;
```

To remove a Fat-Free variable from memory if you no longer need it (discard it so it doesn't interfere with your other functions/methods), use the method:-

``` php
$f3->clear('var'); // or
unset($f3->var);
```

To find out if a variable has been previously defined:-

``` php
$f3->exists('var') //
isset($f3->var)
```

### Globals

F3 maintains its own symbol table for framework and application variables, which are independent of PHP's. Some variables are mapped to PHP globals. Fat-Free's `SESSION` is equivalent to `$_SESSION`, and `REQUEST` maps to `$_REQUEST`. Use of framework variables is recommended, instead of PHP's, to help you with data transfer across different functions, classes and methods. They also have other advantages:-

* You can use framework variables directly in your templates.
* You don't have to instruct PHP to reference a variable outside the current scope using a global keyword inside each function or method. All F3 variables are global to your application.
* Setting the Fat-Free equivalent of a PHP global like `SESSION` also changes PHP's underlying `$_SESSION`. Altering the latter also alters the framework counterpart.

Fat-Free does not maintain just a dumb storage for variables and their values. It can also automate session management and other things. Assigning or retrieving a value through F3's `SESSION` variable auto-starts the session. If you use `$_SESSION` (or session-related functions) directly, instead of the framework variable `SESSION`, your application becomes responsible for managing sessions.

As a rule, framework variables do not persist between HTTP requests. Only `SESSION` and `COOKIE` (and their elements) which are mapped to PHP's `$_SESSION` and `$_COOKIE` global variables are exempt from the stateless nature of HTTP.

There are several predefined global variables used internally by Fat-Free, and you can certainly utilize them in your application. Be sure you know what you're doing. Altering some Fat-Free global variables may result in unexpected framework behavior.

The framework has several variables to help you keep your files and directory structures organized. We've seen how we can automate class loading by using the `AUTOLOAD`. There's a `UI` global variable, which contains the path pointing to the location of your HTML views/templates. `DEBUG` is another variable you'll be using quite often during application development and it's used for setting the verbosity of error traces.

Refer to the [Quick Reference]#quick-reference if you need a comprehensive list of built-in framework variables.

### Naming Rules

A framework variable may contain any number of letters, digits and underscores. It must start with an alpha character and should have no spaces. Variable names are case-sensitive.

F3 uses all-caps for internal predefined global variables. Nothing stops you from using variable names consisting of all-caps in your own program, but as a general rule, stick to lowercase (or camelCase) when you set up your own variables so you can avoid any possible conflict with current and future framework releases.

You should not use PHP reserved words like `if`, `for`, `class`, `default`, etc. as framework variable names. These may cause unpredictable results.

### Working with String and Array Variables

F3 also provides a number of tools to help you with framework variables.

``` php
$f3->set('a','fire');
$f3->concat('a','cracker');
echo $f3->get('a'); // returns the string 'firecracker'

$f3->copy('a','b');
echo $f3->get('b'); // returns the same string: 'firecracker'
```

F3 also provides some primitive methods for working with array variables:-

``` php
$f3->set('colors',['red','blue','yellow']);
$f3->push('colors','green'); // works like PHP's array_push()
echo $f3->pop('colors'); // returns 'green'

$f3->unshift('colors','purple'); // similar to array_unshift()
echo $f3->shift('colors'); // returns 'purple'

$f3->set('grays',['light','dark']);
$result=$f3->merge('colors','grays'); // merges the two arrays
```

### Do-It-Yourself Directory Structures

Unlike other frameworks that have rigid folder structures, F3 gives you a lot of flexibility. You can have a folder structure that looks like this (parenthesized words in all-caps represent the F3 framework variables that need tweaking):-

```
/ (your Web root, where index.php is located)
app/ (application files)
    dict/ (LOCALES, optional)
    controllers/
    logs/ (LOGS, optional)
    models/
    views/ (UI)
css/
js/
lib/ (you can store base.php here)
tmp/ (TEMP, used by the framework)
   cache/ (CACHE)
```

Feel free to organize your files and directories any way you want. Just set the appropriate F3 global variables. If you want a really secure site, Fat-Free even allows you to store all your files in a non-Web-accessible directory. The only requirement is that you leave `index.php`, `.htaccess` and your public files, like CSS, JavaScript, images, etc. in a path visible to your browser.

### About the F3 Error Handler

Fat-Free generates its own HTML error pages, with stack traces to help you with debugging. Here's an example:-

> ---
> ### Internal Server Error
> strpos() expects at least 2 parameters, 0 given
>
>     • var/html/dev/main.php:96 strpos()
>     • var/html/dev/index.php:16 Base->run()
> ---

If you feel it's a bit too plain or wish to do other things when the error occurs, you may create your own custom error handler:-

``` php
$f3->set('ONERROR',
    function($f3) {
        // custom error handler code goes here
        // use this if you want to display errors in a
        // format consistent with your site's theme
        echo $f3->get('ERROR.status');
    }
);
```

F3 maintains a global variable containing the details of the latest error that occurred in your application. The `ERROR` variable is an array structured as follows:-

```
ERROR.code - displays the error code (404, 500, etc.)
ERROR.status - header and page title
ERROR.text - error context
ERROR.trace - stack trace
```

While developing your application, it's best to set the debug level to maximum so you can trace all errors to their root cause:-

``` php
$f3->set('DEBUG',3);
```

Just insert the command in your application's bootstrap sequence.

Once your application is ready for release, simply remove the statement from your application, or replace it with:-

``` php
$f3->set('DEBUG',0);
```

This will suppress the stack trace output in any system-generated HTML error page (because it's not meant to be seen by your site visitors).

`DEBUG` can have values ranging from 0 (stack trace suppressed) to 3 (most verbose).

**Don't forget!** Stack traces may contain paths, file names, database commands, user names and passwords. You might expose your Web site to unnecessary security risks if you fail to set the `DEBUG` global variable to 0 in a production environment.

### Configuration Files

If your application needs to be user-configurable, F3 provides a handy method for reading configuration files to set up your application. This way, you and your users can tweak the application without altering any PHP code.

Instead of creating a PHP script that contains the following sample code:-

``` php
$f3->set('num',123);
$f3->set('str','abc');
$f3->set('hash',['x'=>1,'y'=>2,'z'=>3]);
$f3->set('items',[7,8,9]);
$f3->set('mix',['this',123.45,FALSE]);
```

You can construct a configuration file that does the same thing:-

``` ini
[globals]
num=123
; this is a regular string
str=abc
; another way of assigning strings
str="abc"
; this is an array
hash[x]=1
hash[y]=2
hash[z]=3
; dot-notation is recognized too
hash.x=1
hash.y=2
hash.z=3
; this is also an array
items=7,8,9
; array with mixed elements
mix="this",123.45,FALSE
```

Instead of lengthy `$f3->set()` statements in your code, you can instruct the framework to load a configuration file as code substitute. Let's save the above text as setup.cfg. We can then call it with a simple:-

``` php
$f3->config('setup.cfg');
```

String values need not be quoted, unless you want leading or trailing spaces included. If a comma should be treated as part of a string, enclose the string using double-quotes - otherwise, the value will be treated as an array (the comma is used as an array element separator). Strings can span multiple lines:-

``` ini
[globals]
str="this is a \
very long \
string"
```

F3 also gives you the ability to define HTTP routes in configuration files:-

``` ini
[routes]
GET /=home
GET /404=App->page404
GET /page/@num=Page->@controller
```

Route maps can be defined in configuration files too:-

``` ini
[maps]
/blog=Blog\Login
/blog/@controller=Blog\@controller
```

The `[globals]`, `[routes]`, and `[maps]` section headers are required. You can combine both sections in a single configuration file - although having `[routes]` and `[maps]` in a separate file is recommended. This way you can allow end-users to modify some application-specific flags, and at the same time restrict them from meddling with your routing logic.

## Views and Templates

### Separation of Concerns

A user interface like an HTML page should be independent of the underlying PHP code related to routing and business logic. This is fundamental to the MVC paradigm. A basic revision like converting `<h3>` to `<p>` should not demand a change in your application code. In the same manner, transforming a simple route like `GET /about` to `GET /about-us` should not have any effect on your user interface and business logic, (the view and model in MVC, or representation and method in RMR).

Mixing programming constructs and user interface components in a single file, like spaghetti coding, makes future application maintenance a nightmare.

### PHP as a Template Engine

F3 supports PHP as a template engine. Take a look at this HTML fragment saved as `template.htm`:-.

``` html
<p>Hello, <?php echo $name; ?>!</p>
```

If short tags are enabled on your server, this should work too:-

``` html
<p>Hello, <?= $name ?></p>
```

To display this template, you can have PHP code that looks like this (stored in a file separate from the template):-

``` php
$f3=require('lib/base.php');
$f3->route('GET /',
    function($f3) {
        $f3->set('name','world');
        $view=new View;
        echo $view->render('template.htm');
        // Previous two lines can be shortened to:-
        // echo View::instance()->render('template.htm');
    }
);
$f3->run();
```

The only issue with using PHP as a template engine, due to the embedded PHP code in these files, is the conscious effort needed to stick to the guidelines on separation of concerns and resist the temptation of mixing business logic with your user interface.

### A Quick Look at the F3 Template Language

As an alternative to PHP, you can use F3's own template engine. The above HTML fragment can be rewritten as:-

``` html
<p>Hello, {{ @name }}!</p>
```

and the code needed to view this template:-

``` php
$f3=require('lib/base.php');
$f3->route('GET /',
    function($f3) {
        $f3->set('name','world');
        $template=new Template;
        echo $template->render('template.htm');
        // Above lines can be written as:-
        // echo Template::instance()->render('template.htm');
    }
);
$f3->run();
```

Like routing tokens used for catching variables in URLs (still remember the `GET /brew/@count` example in the previous section?), F3 template tokens begin with the `@` symbol followed by a series of letters and digits enclosed in curly braces. The first character must be alpha. Template tokens have a one-to-one correspondence with framework variables. The framework automatically replaces a token with the value stored in a variable of the same name.

In our example, F3 replaces the `@name` token in our template with the value we assigned to the name variable. At runtime, the output of the above code will be:-

``` html
<p>Hello, world</p>
```

Worried about performance of F3 templates? At runtime, the framework parses and compiles/converts an F3 template to PHP code the first time it's displayed via `$template->render()`. The framework then uses this compiled code in all subsequent calls. Hence, performance should be the same as PHP templates, if not better due to code optimization done by the template compiler when more complex templates are involved.

Whether you use PHP's template engine or F3's own, template rendering can be significantly faster if you have APC, WinCache or XCache available on your server.

As mentioned earlier, framework variables can hold any PHP data type. However, usage of non-scalar data types in F3 templates may produce strange results if you're not careful. Expressions in curly braces will always be evaluated and converted to string. You should limit your user interface variables to simple scalars:- `string`, `integer`, `boolean` or `float` data types.

But what about arrays? Fat-Free recognizes arrays and you can employ them in your templates. You can have something like:-

``` html
<p>{{ @buddy[0] }}, {{ @buddy[1] }}, and {{ @buddy[2] }}</p>
```

And populate the `@buddy` array in your PHP code before serving the template:-

``` php
$f3->set('buddy',['Tom','Dick','Harry']);
```

However, if you simply insert `{{ @buddy }}` in your template, PHP 5.4 will replace it with `'Array'` because it converts the token to a string. PHP 5.4, on the other hand, will generate an `Array to string conversion` notice at runtime.

F3 allows you to embed expressions in templates. These expressions may take on various forms, like arithmetic calculations, boolean expressions, PHP constants, etc. Here are a few examples:-

``` html
{{ 2*(@page-1) }}
{{ (int)765.29+1.2e3 }}
<option value="F" {{ @active?'selected="selected"':'' }}>Female</option>
{{ var_dump(@xyz) }}
<p>That is {{ preg_match('/Yes/i',@response)?'correct':'wrong' }}!</p>
{{ @obj->property }}
```

An additional note about array expressions: Take note that `@foo.@bar` is a string concatenation `$foo.$bar`), whereas `@foo.bar` translates to `$foo['bar']`. If `$foo[$bar]` is what you intended, use the `@foo[@bar]` regular notation.

Framework variables may also contain anonymous functions:

``` php
$f3->set('func',
    function($a,$b) {
        return $a.', '.$b;
    }
);
```

The F3 template engine will interpret the token as expected, if you specify the following expression:

``` html
{{ @func('hello','world') }}
```

### Templates Within Templates

Simple variable substitution is one thing all template engines have. Fat-Free has more up its sleeves:-

``` html
<include href="header.htm" />
```

The <include> directive will embed the contents of the header.htm template at the exact position where the directive is stated. You can also have dynamic content in the form of:-

``` html
<include href="{{ @content }}" />
```

A practical use for such template directive is when you have several pages with a common HTML layout but with different content. Instructing the framework to insert a sub-template into your main template is as simple as writing the following PHP code:-

``` php
// switch content to your blog sub-template
$f3->set('content','blog.htm');
// in another route, switch content to the wiki sub-template
$f3->set('content','wiki.htm');
```

A sub-template may in turn contain any number of <include> directives. F3 allows unlimited nested templates.

You can specify filenames with something other than .htm or .html file extensions, but it's easier to preview them in your Web browser during the development and debugging phase. The template engine is not limited to rendering HTML files. In fact you can use the template engine to render other kinds of files.

The `<include>` directive also has an optional `if` attribute so you can specify a condition that needs to be satisfied before the sub-template is inserted:-

``` html
<include if="{{ count(@items) }}" href="items.htm" />
```

### Exclusion of Segments

During the course of writing/debugging F3-powered programs and designing templates, there may be instances when disabling the display of a block of HTML may be handy. You can use the `<exclude>` directive for this purpose:-

``` html
<exclude>
    <p>A chunk of HTML we don't want displayed at the moment</p>
</exclude>
```

That's like the `<!-- comment -->` HTML comment tag, but the `<exclude>` directive makes the HTML block totally invisible once the template is rendered.

Here's another way of excluding template content or adding comments:-

``` html
{* <p>A chunk of HTML we don't want displayed at the moment</p> *}
```

### Conditional Segments

Another useful template feature is the `<check>` directive. It allows you to embed an HTML fragment depending on the evaluation of a certain condition. Here are a few examples:-

``` html
<check if="{{ @page=='Home' }}">
    <false><span>Inserted if condition is false</span></false>
</check>
<check if="{{ @gender=='M' }}">
    <true>
        <div>Appears when condition is true</div>
    </true>
    <false>
        <div>Appears when condition is false</div>
    </false>
</check>
```

You can have as many nested `<check>` directives as you need.

An F3 expression inside an if attribute that equates to `NULL`, an empty string, a boolean `FALSE`, an empty array or zero, automatically invokes `<false>`. If your template has no `<false>` block, then the `<true>` opening and closing tags are optional:-

``` html
<check if="{{ @loggedin }}">
    <p>HTML chunk to be included if condition is true</p>
</check>
```

### Repeating Segments

Fat-Free can also handle repetitive HTML blocks:-

``` html
<repeat group="{{ @fruits }}" value="{{ @fruit }}">
    <p>{{ trim(@fruit) }}</p>
</repeat>
```

The `group` attribute `@fruits` inside the `<repeat>` directive must be an array and should be set in your PHP code accordingly:-

``` php
$f3->set('fruits',['apple','orange ',' banana']);
```

Nothing is gained by assigning a value to `@fruit` in your application code. Fat-Free ignores any preset value it may have because it uses the variable to represent the current item during iteration over the group. The output of the above HTML template fragment and the corresponding PHP code becomes:-

``` html
<p>apple</p>
<p>orange</p>
<p>banana</p>
```

The framework allows unlimited nesting of `<repeat>` blocks:-

``` html
<repeat group="{{ @div }}" key="{{ @ikey }}" value="{{ @idiv }}">
    <div>
        <p><span><b>{{ @ikey }}</b></span></p>
        <p>
        <repeat group="{{ @idiv }}" value="{{ @ispan }}">
            <span>{{ @ispan }}</span>
        </repeat>
        </p>
    </div>
</repeat>
```

Apply the following F3 command:-

``` php
$f3->set('div',
    [
        'coffee'=>['arabica','barako','liberica','kopiluwak'],
        'tea'=>['darjeeling','pekoe','samovar']
    ]
);
```

As a result, you get the following HTML fragment:-

``` html
<div>
    <p><span><b>coffee</b></span></p>
    <p>
        <span>arabica</span>
        <span>barako</span>
        <span>liberica</span>
        <span>kopiluwak</span>
    <p>
</div>
<div>
    <p><span><b>tea</b></span></p>
    <p>
        <span>darjeeling</span>
        <span>pekoe</span>
        <span>samovar</span>
    </p>
</div>
```

Amazing, isn't it? And the only thing you had to do in PHP was to define the contents of a single F3 variable `div` to replace the `@div` token. Fat-Free makes both programming and Web template design really easy.

The `<repeat>` template directive's `value` attribute returns the value of the current element in the iteration. If you need to get the array key of the current element, use the `key` attribute instead. The `key` attribute is optional.

`<repeat>` also has an optional counter attribute that can be used as follows:-

``` html
<repeat group="{{ @fruits }}" value="{{ @fruit }}" counter="{{ @ctr }}">
    <p class="{{ @ctr%2?'odd':'even' }}">{{ trim(@fruit) }}</p>
</repeat>
```

Internally, F3's template engine records the number of loop iterations and saves that value in the variable/token `@ctr`, which is used in our example to determine the odd/even classification.

### Embedding Javascript and CSS

If you have to insert F3 tokens inside a `<script>` or `<style>` section of your template, the framework will still replace them the usual way:-

``` html
<script type="text/javascript">
    function notify() {
        alert('You are logged in as: {{ @userID }}');
    }
</script>
```

Embedding template directives inside your `<script>` or `<style>` tags requires no special handling:-

``` html
<script type="text/javascript">
	var discounts=[];
    <repeat group="{{ @rates }}" value="{{ @rate }}">
        // whatever you want to repeat in Javascript, e.g.
        discounts.push("{{ @rate }}");
    </repeat>
</script>
```

### Document Encoding

By default, Fat-Free uses the UTF-8 character set unless changed. You can override this behavior by issuing something like:-

``` php
$f3->set('ENCODING','ISO-8859-1');
```

Once you inform the framework of the desired character set, F3 will use it in all HTML and XML templates until altered again.

### All Kinds of Templates

As mentioned earlier in this section, the framework isn't limited to HTML templates. You can process XML templates just as well. The mechanics are pretty much similar. You still have the same `{{ @variable }}` and `{{ expression }}` tokens, `<repeat>`, `<check>`, `<include>`, and `<exclude>` directives at your disposal. Just tell F3 that you're passing an XML file instead of HTML:-

``` php
echo Template::instance()->render('template.xml','application/xml');
```

The second argument represents the MIME type of the document being rendered.

The View component of MVC covers everything that doesn't fall under the Model and Controller, which means your presentation can and should include all kinds of user interfaces, like RSS, e-mail, RDF, FOAF, text files, etc. The example below shows you how to separate your e-mail presentation from your application's business logic:-

``` html
MIME-Version: 1.0
Content-type: text/html; charset={{ @ENCODING }}
From: {{ @from }}
To: {{ @to }}
Subject: {{ @subject }}

<p>Welcome, and thanks for joining {{ @site }}!</p>
```

Save the above e-mail template as welcome.txt. The associated F3 code would be:-

``` php
$f3->set('from','');
$f3->set('to','');
$f3->set('subject','Welcome');
ini_set('sendmail_from',$f3->get('from'));
mail(
    $f3->get('to'),
    $f3->get('subject'),
    Template::instance()->render('email.txt','text/html')
);
```

Tip: Replace the SMTP mail() function with imap_mail() if your script communicates with an IMAP server.

Now isn't that something? Of course, if you have a bundle of e-mail recipients, you'd be using a database to populate the firstName, lastName, and email tokens.

Here's an alternative solution using the F3's SMTP plug-in:-

``` php
$mail=new SMTP('smtp.gmail.com',465,'SSL','account@gmail.com','secret');
$mail->set('from','');
$mail->set('to','"Slasher" ');
$mail->set('subject','Welcome');
$mail->send(Template::instance()->render('email.txt'));
```

### Multilingual Support

F3 supports multiple languages right out of the box.

First, create a dictionary file with the following structure (one file per language):-

``` php
<?php
return [
    'love'=>'I love F3',
    'today'=>'Today is {0,date}',
    'pi'=>'{0,number}',
    'money'=>'Amount remaining: {0,number,currency}'
];
```

Save it as `dict/en.php`. Let's create another dictionary, this time for German. Save the file as `dict/de.php`:-

``` php
<?php
return [
    'love'=>'Ich liebe F3',
    'today'=>'Heute ist {0,date}',
    'money'=>'Restbetrag: {0,number,currency}'
];
```

Dictionaries are nothing more than key-value pairs. F3 automatically instantiates framework variables based on the keys in the language files. As such, it's easy to embed these variables as tokens in your templates. Using the F3 template engine:-

``` html
<h1>{{ @love }}</h1>
<p>
{{ @today,time() | format }}.<br />
{{ @money,365.25 | format }}<br />
{{ @pi }}
</p>
```

And the longer version that utilizes PHP as a template engine:-

``` php
<?php $f3=Base::instance(); ?>
<h1><?php echo $f3->get('love'); ?></h1>
<p>
    <?php echo $f3->get('today',time()); ?>.<br />
    <?php echo $f3->get('money',365.25); ?>
    <?php echo $f3->get('pi'); ?>
</p>
```

Next, we instruct F3 to look for dictionaries in the `dict/` folder:-

``` php
$f3->set('LOCALES','dict/');
```

But how does the framework determine which language to use? F3 will detect it automatically by looking at the HTTP request headers first, specifically the `Accept-Language` header sent by the browser.

To override this behavior, you can trigger F3 to use a language specified by the user or application:-

``` php
$f3->set('LANGUAGE','de');
```

**Note:** In the above example, the key pi exists only in the English dictionary. The framework will always use English (`en`) as a fallback to populate keys that are not present in the specified (or detected) language.

You may also create dictionary files for language variants like `en-US`, `es-AR`, etc. In this case, F3 will use the language variant first (like `es-AR`). If there are keys that do not exist in the variant, the framework will look up the key in the root language (`es`), then use the `en` language file as the final fallback.
Dictionary key-value pairs become F3 variables once referenced. Make sure the keys do not conflict with any framework variable instantiated via `$f3->set()`, `$f3->mset()`, or `$f3->config()`.

Did you notice the peculiar `'Today is {0,date}'` pattern in our previous example? F3's multilingual capability hinges on string/message formatting rules of the ICU project. The framework uses its own subset of the ICU string formatting implementation. There is no need for PHP's `intl` extension to be activated on the server.

One more thing: F3 can also load .ini-style formatted files as dictionaries:-

``` ini
love="I love F3"
today="Today is {0,date}"
pi="{0,number}"
money="Amount remaining: {0,number,currency}"
```

Save it as `dict/en.ini` so the framework can load it automatically.

### Data Sanitation

By default, both view handler and template engine escapes all rendered variables, i.e. converted to HTML entities to protect you from possible XSS and code injection attacks. On the other hand, if you wish to pass valid HTML fragments from your application code to your template:-

``` php
$f3->set('ESCAPE',FALSE);
```

This may have undesirable effects. You might not want all variables to pass through unescaped. Fat-Free allows you to unescape variables individually. For F3 templates:-

``` html
{{ @html_content | raw }}
```

In the case of PHP templates:-

``` php
<?php echo View::instance()->raw($html_content); ?>
```

As an addition to auto-escaping of F3 variables, the framework also gives you a free hand at sanitizing user input from HTML forms:-

``` php
$f3->scrub($_GET,'p; br; span; div; a');
```

This command will strip all tags (except those specified in the second argument) and unsafe characters from the specified variable. If the variable contains an array, each element in the array is sanitized recursively. If an asterisk (*) is passed as the second argument, `$f3->scrub()` permits all HTML tags to pass through untouched and simply remove unsafe control characters.

## Databases

### Connecting to a Database Engine

Fat-Free is designed to make the job of interfacing with SQL databases a breeze. If you're not the type to immerse yourself in details about SQL, but lean more towards object-oriented data handling, you can go directly to the next section of this tutorial. However, if you need to do some complex data-handling and database performance optimization tasks, SQL is the way to go.

Establishing communication with a SQL engine like MySQL, SQLite, SQL Server, Sybase, and Oracle is done using the familiar `$f3->set()` command. Connecting to a SQLite database would be:-

``` php
$db=new DB\SQL('sqlite:/absolute/path/to/your/database.sqlite');
```

Another example, this time with MySQL:-

``` php
$db=new DB\SQL(
    'mysql:host=localhost;port=3306;dbname=mysqldb',
    'admin',
    'p455w0rD'
);
```

### Querying the Database

OK. That was easy, wasn't it? That's pretty much how you would do the same thing in ordinary PHP. You just need to know the DSN format of the database you're connecting to. See the PDO section of the PHP manual.

Let's continue our PHP code:-

``` php
$f3->set('result',$db->exec('SELECT brandName FROM wherever'));
echo Template::instance()->render('abc.htm');
```

Huh, what's going on here? Shouldn't we be setting up things like PDOs, statements, cursors, etc.? The simple answer is: you don't have to. F3 simplifies everything by taking care of all the hard work in the backend.

This time we create an HTML template like `abc.htm` that has at a minimum the following:-

``` html
<repeat group="{{ @result }}" value="{{ @item }}">
    <span>{{ @item.brandName  }}</span>
</repeat>
```

In most instances, the SQL command set should be enough to generate a Web-ready result so you can use the `result` array variable in your template directly. Be that as it may, Fat-Free will not stop you from getting into its SQL handler internals. In fact, F3's `DB\SQL` class derives directly from PHP's `PDO` class, so you still have access to the underlying PDO components and primitives involved in each process, if you need some fine-grain control.

### Transactions

Here's another example. Instead of a single statement provided as an argument to the `$db->exec()` command, you can also pass an array of SQL statements:-

``` php
$db->exec(
    [
        'DELETE FROM diet WHERE food="cola"',
        'INSERT INTO diet (food) VALUES ("carrot")',
        'SELECT * FROM diet'
    ]
);
```

F3 is smart enough to know that if you're passing an array of SQL instructions, this indicates a SQL batch transaction. You don't have to worry about SQL rollbacks and commits because the framework will automatically revert to the initial state of the database if any error occurs during the transaction. If successful, F3 commits all changes made to the database.

You can also start and end a transaction programmatically:-

``` php
$db->begin();
$db->exec('DELETE FROM diet WHERE food="cola"');
$db->exec('INSERT INTO diet (food) VALUES ("carrot")');
$db->exec('SELECT * FROM diet');
$db->commit();
```

A rollback will occur if any of the statements encounter an error.

To get a list of all database instructions issued:-

``` php
echo $db->log();
```

### Parameterized Queries

Passing string arguments to SQL statements is fraught with danger. Consider this:-

``` php
$db->exec(
    'SELECT * FROM users '.
    'WHERE username="'.$f3->get('POST.userID'.'"')
);
```

If the `POST` variable `userID` does not go through any data sanitation process, a malicious user can pass the following string and damage your database irreversibly:-

``` sql
admin"; DELETE FROM users; SELECT "1
```

Luckily, parameterized queries help you mitigate these risks:-

``` php
$db->exec(
    'SELECT * FROM users WHERE userID=?',
    $f3->get('POST.userID')
);
```

If F3 detects that the value of the query parameter/token is a string, the underlying data access layer escapes the string and adds quotes as necessary.

Our example in the previous section will be a lot safer from SQL injection if written this way:-

``` php
$db->exec(
    [
        'DELETE FROM diet WHERE food=:name',
        'INSERT INTO diet (food) VALUES (?)',
        'SELECT * FROM diet'
    ],
    [
        array(':name'=>'cola'),
        array(1=>'carrot'),
        NULL
    ]
);
```

### CRUD (But With a Lot of Style)

F3 is packed with easy-to-use object-relational mappers (ORMs) that sit between your application and your data - making it a lot easier and faster for you to write programs that handle common data operations - like creating, retrieving, updating, and deleting (CRUD) information from SQL and NoSQL databases. Data mappers do most of the work by mapping PHP object interactions to the corresponding backend queries.

Suppose you have an existing MySQL database containing a table of users of your application. (SQLite, PostgreSQL, SQL Server, Sybase will do just as well.) It would have been created using the following SQL command:-

``` sql
CREATE TABLE users (
    userID VARCHAR(30),
    password VARCHAR(30),
    visits INT,
    PRIMARY KEY(userID)
);
```

**Note:** MongoDB is a NoSQL database engine and inherently schema-less. F3 has its own fast and lightweight NoSQL implementation called Jig, which uses PHP-serialized or JSON-encoded flat files. These abstraction layers require no rigid data structures. Fields may vary from one record to another. They can also be defined or dropped on the fly.

Now back to SQL. First, we establish communication with our database.

``` php
$db=new DB\SQL(
    'mysql:host=localhost;port=3306;dbname=mysqldb',
    'admin',
    'wh4t3v3r'
);
```

To retrieve a record from our table:-

``` php
$user=new DB\SQL\Mapper($db,'users');
$user->load(['userID=?','tarzan']);
```

The first line instantiates a data mapper object that interacts with the `users` table in our database. Behind the scene, F3 retrieves the structure of the `users` table and determines which field(s) are defined as primary key(s). At this point, the mapper object contains no data yet (dry state) so `$user` is nothing more than a structured object - but it contains the methods it needs to perform the basic CRUD operations and some extras. To retrieve a record from our users table with a `userID` field containing the string value `tarzan`, we use the `load() method`. This process is called "auto-hydrating" the data mapper object.

Easy, wasn't it? F3 understands that a SQL table already has a structural definition existing within the database engine itself. Unlike other frameworks, F3 requires no extra class declarations (unless you want to extend the data mappers to fit complex objects), no redundant PHP array/object property-to-field mappings (duplication of efforts), no code generators (which require code regeneration if the database structure changes), no stupid XML/YAML files to configure your models, no superfluous commands just to retrieve a single record. With F3, a simple resizing of a `varchar` field in MySQL does not demand a change in your application code. Consistent with MVC and "separation of concerns", the database admin has as much control over the data (and the structures) as a template designer has over HTML/XML templates.

If you prefer working with NoSQL databases, the similarities in query syntax are superficial. In the case of the MongoDB data mapper, the equivalent code would be:-

``` php
$db=new DB\Mongo('mongodb://localhost:27017','testdb');
$user=new DB\Mongo\Mapper($db,'users');
$user->load(['userID'=>'tarzan']);
```

With Jig, the syntax is similar to F3's template engine:-

``` php
$db=new DB\Jig('db/data/',DB\Jig::FORMAT_JSON);
$user=new DB\Jig\Mapper($db,'users');
$user->load(['@userID=?','tarzan']);
```

### The Smart SQL ORM

The framework automatically maps the field `visits` in our table to a data mapper property during object instantiation, i.e. `$user=new DB\SQL\Mapper($db,'users');`. Once the object is created, `$user->password` and `$user->userID` would map to the `password` and `userID` fields in our table, respectively.

You can't add or delete a mapped field, or change a table's structure using the ORM. You must do this in MySQL, or whatever database engine you're using. After you make the changes in your database engine, Fat-Free will automatically synchronize the new table structure with your data mapper object when you run your application.

F3 derives the data mapper structure directly from the database schema. No guesswork involved. It understands the differences between MySQL, SQLite, MSSQL, Sybase, and PostgreSQL database engines.

SQL identifiers should not use reserved words, and should be limited to alphanumeric characters `A-Z`, `0-9`, and the underscore symbol (`_`). Column names containing spaces (or special characters) and surrounded by quotes in the data definition are not compatible with the ORM. They cannot be represented properly as PHP object properties.

Let's say we want to increment the user's number of visits and update the corresponding record in our users table, we can add the following code:-

``` php
$user->visits++;
$user->save();
```

If we wanted to insert a record, we follow this process:-

``` php
$user=new DB\SQL\Mapper($db,'users');
// or $user=new DB\Mongo\Mapper($db,'users');
// or $user=new DB\Jig\Mapper($db,'users');
$user->userID='jane';
$user->password=md5('secret');
$user->visits=0;
$user->save();
```

We still use the same `save()` method. But how does F3 know when a record should be inserted or updated? At the time a data mapper object is auto-hydrated by a record retrieval, the framework keeps track of the record's primary keys (or `_id`, in the case of MongoDB and Jig) - so it knows which record should be updated or deleted - even when the values of the primary keys are changed. A programmatically-hydrated data mapper - the values of which were not retrieved from the database, but populated by the application - will not have any memory of previous values in its primary keys. The same applies to MongoDB and Jig, but using object `_id` as reference. So, when we instantiated the `$user` object above and populated its properties with values from our program - without at all retrieving a record from the user table, F3 knows that it should insert this record.

A mapper object will not be empty after a `save()`. If you wish to add a new record to your database, you must first dehydrate the mapper:-

``` php
$user->reset();
$user->userID='cheetah';
$user->password=md5('unknown');
$user->save();
```

Calling `save()` a second time without invoking `reset()` will simply update the record currently pointed to by the mapper.

### Caveat for SQL Tables

Although the issue of having primary keys in all tables in your database is argumentative, F3 does not stop you from creating a data mapper object that communicates with a table containing no primary keys. The only drawback is: you can't delete or update a mapped record because there's absolutely no way for F3 to determine which record you're referring to plus the fact that positional references are not reliable. Row IDs are not portable across different SQL engines and may not be returned by the PHP database driver.

To remove a mapped record from our table, invoke the `erase()` method on an auto-hydrated data mapper. For example:-

``` php
$user=new DB\SQL\Mapper($db,'users');
$user->load(['userID=? AND password=?','cheetah','ch1mp']);
$user->erase();
```

Jig's query syntax would be slightly similar:-

``` php
$user=new DB\Jig\Mapper($db,'users');
$user->load(['@userID=? AND @password=?','cheetah','chimp']);
$user->erase();
```

And the MongoDB equivalent would be:-

``` php
$user=new DB\Mongo\Mapper($db,'users');
$user->load(['userID'=>'cheetah','password'=>'chimp']);
$user->erase();
```

### The Weather Report

To find out whether our data mapper was hydrated or not:-

``` php
if ($user->dry())
    echo 'No record matching criteria';
```

### Beyond CRUD

We've covered the CRUD handlers. There are some extra methods that you might find useful:-

``` php
$f3->set('user',new DB\SQL\Mapper($db,'users'));
$f3->get('user')->copyFrom('POST');
$f3->get('user')->save();
```

Notice that we can also use Fat-Free variables as containers for mapper objects.
The `copyFrom()` method hydrates the mapper object with elements from a framework array variable, the array keys of which must have names identical to the mapper object properties, which in turn correspond to the record's field names. So, when a Web form is submitted (assuming the HTML name attribute is set to `userID`), the contents of that input field is transferred to `$_POST['userID']`, duplicated by F3 in its `POST.userID` variable, and saved to the mapped field `$user->userID` in the database. The process becomes very simple if they all have identically-named elements. Consistency in array keys, i.e. template token names, framework variable names and field names is key :)

On the other hand, if we wanted to retrieve a record and copy the field values to a framework variable for later use, like template rendering:-

``` php
$f3->set('user',new DB\SQL\Mapper($db,'users'));
$f3->get('user')->load(['userID=?','jane']);
$f3->get('user')->copyTo('POST');
```

We can then assign {{ @POST.userID }} to the same input field's value attribute. To sum up, the HTML input field will look like this:-

``` html
<input type="text" name="userID" value="{{ @POST.userID }}"/>
```

The `save()`, `update()`, `copyFrom()` data mapper methods and the parameterized variants of `load()` and `erase()` are safe from SQL injection.

### Navigation and Pagination

By default, a data mapper's `load()` method retrieves only the first record that matches the specified criteria. If you have more than one that meets the same condition as the first record loaded, you can use the `skip()` method for navigation:-

``` php
$user=new DB\SQL\Mapper($db,'users');
$user->load('visits>3');
// Rewritten as a parameterized query
$user->load(['visits>?',3]);

// For MongoDB users:-
// $user=new DB\Mongo\Mapper($db,'users');
// $user->load(['visits'=>['$gt'=>3]]);

// If you prefer Jig:-
// $user=new DB\Jig\Mapper($db,'users');
// $user->load('@visits>?',3);

// Display the userID of the first record that matches the criteria
echo $user->userID;
// Go to the next record that matches the same criteria
$user->skip(); // Same as $user->skip(1);
// Back to the first record
$user->skip(-1);
// Move three records forward
$user->skip(3);
```

You may use `$user->next()` as a substitute for `$user->skip()`, and `$user->prev()` if you think it gives more meaning to `$user->skip(-1)`.

Use the `dry()` method to check if you've maneuvered beyond the limits of the result set. `dry()` will return TRUE if you try `skip(-1)` on the first record. It will also return TRUE if you `skip(1)` on the last record that meets the retrieval criteria.

The `load()` method accepts a second argument: an array of options containing key-value pairs such as:-

``` php
$user->load(
    ['visits>?',3],
    [
        'order'=>'userID DESC'
        'offset'=>5,
        'limit'=>3
    ]
);
```

If you're using MySQL, the query translates to:-

``` mysql
SELECT * FROM users
WHERE visits>3
ORDER BY userID DESC
LIMIT 3 OFFSET 5;
```

This is one way of presenting data in small chunks. Here's another way of paginating results:-

``` php
$page=$user->paginate(2,5,['visits>?',3]);
```

In the above scenario, F3 will retrieve records that match the criteria `'visits>3'`. It will then limit the results to 5 records (per page) starting at page offset 2 (0-based). The framework will return an array consisting of the following elements:-

```
[subset] array of mapper objects that match the criteria
[count] number of subsets available
[pos] actual subset position
```

The actual subset position returned will be NULL if the first argument of `paginate()` is a negative number or exceeds the number of subsets found.

### Virtual Fields

There are instances when you need to retrieve a computed value of a field, or a cross-referenced value from another table. Enter virtual fields. The SQL mini-ORM allows you to work on data derived from existing fields.

Suppose we have the following table defined as:-

``` sql
CREATE TABLE products
    productID VARCHAR(30),
    description VARCHAR(255),
    supplierID VARCHAR(30),
    unitprice DECIMAL(10,2),
    quantity INT,
    PRIMARY KEY(productID)
);
```

No `totalprice` field exists, so we can tell the framework to request from the database engine the arithmetic product of the two fields:-

``` php
$item=new DB\SQL\Mapper($db,'products');
$item->totalprice='unitprice*quantity';
$item->load(['productID=:pid',':pid'=>'apple']);
echo $item->totalprice;
```

The above code snippet defines a virtual field called `totalprice` which is computed by multiplying `unitprice` by the `quantity`. The SQL mapper saves that rule/formula, so when the time comes to retrieve the record from the database, we can use the virtual field like a regular mapped field.

You can have more complex virtual fields:-

``` php
$item->mostNumber='MAX(quantity)';
$item->load();
echo $item->mostNumber;
```

This time the framework retrieves the product with the highest quantity (notice the `load()` method does not define any criteria, so all records in the table will be processed). Of course, the virtual field `mostNumber` will still give you the right figure if you wish to limit the expression to a specific group of records that match a specified criteria.

You can also derive a value from another table:-

``` php
$item->supplierName=
    'SELECT name FROM suppliers '.
    'WHERE products.supplierID=suppliers.supplierID';
$item->load();
echo $item->supplierName;
```

Every time you load a record from the products table, the ORM cross-references the `supplerID` in the `products` table with the `supplierID` in the `suppliers` table.

To destroy a virtual field, use `unset($item->totalPrice);`. The `isset($item->totalPrice)` expression returns TRUE if the `totalPrice` virtual field was defined, or FALSE if otherwise.

Remember that a virtual field must be defined prior to data retrieval. The ORM does not perform the actual computation, nor the derivation of results from another table. It is the database engine that does all the hard work.

### Seek and You Shall Find

If you have no need for record-by-record navigation, you can retrieve an entire batch of records in one shot:-

``` php
$frequentUsers=$user->find(['visits>?',3],['order'=>'userID']);
```

Jig mapper's query syntax has a slight resemblance:-

``` php
$frequentUsers=$user->find(['@visits>?',3],['order'=>'userID']);
```

The equivalent code using the MongoDB mapper:-

``` php
$frequentUsers=$user->find(['visits'=>['$gt'=>3]],['userID'=>1]);
```

The `find()` method searches the `users` table for records that match the criteria, sorts the result by `userID` and returns the result as an array of mapper objects. `find('visits>3')` is different from `load('visits>3')`. The latter refers to the current `$user` object. `find()` does not have any effect on `skip()`.

**Important:** Declaring an empty condition, NULL, or a zero-length string as the first argument of `find()` or `load()` will retrieve all records. Be sure you know what you're doing - you might exceed PHP's memory_limit on large tables or collections.

The `find()` method has the following syntax:-

``` php
find(
    $criteria,
    [
        'group'=>'foo',
        'order'=>'foo,bar',
        'limit'=>5,
        'offset'=>0
    ]
);
```

find() returns an array of objects. Each object is a mapper to a record that matches the specified criteria.:-

``` php
$place=new DB\SQL\Mapper($db,'places');
$list=$place->find('state="New York"');
foreach ($list as $obj)
    echo $obj->city.', '.$obj->country;
```

If you need to convert a mapper object to an associative array, use the `cast()` method:-

``` php
$array=$place->cast();
echo $array['city'].', '.$array['country'];
```

To retrieve the number of records in a table that match a certain condition, use the `count()` method.

``` php
if (!$user->count(['visits>?',10]))
    echo 'We need a better ad campaign!';
```

There's also a `select()` method that's similar to `find()` but provides more fine-grained control over fields returned. It has a SQL-like syntax:-

``` php
select(
    'foo, bar, MIN(baz) AS lowest',
    'foo > ?',
    [
        'group'=>'foo, bar',
        'order'=>'baz ASC',
        'limit'=>5,
        'offset'=>3
    ]
);
```

Much like the `find()` method, `select()` does not alter the mapper object's contents. It only serves as a convenience method for querying a mapped table. The return value of both methods is an array of mapper objects. Using `dry()` to determine whether a record was found by an of these methods is inappropriate. If no records match the `find()` or `select()` criteria, the return value is an empty array.

### Profiling

If you ever want to find out which SQL statements issued directly by your application (or indirectly thru mapper objects) are causing performance bottlenecks, you can do so with a simple:-

``` php
echo $db->log();
```

F3 keeps track of all commands issued to the underlying SQL database driver, as well as the time it takes for each statement to complete - just the right information you need to tweak application performance.

### Sometimes It Just Ain't Enough

In most cases, you can live by the comforts given by the data mapper methods we've discussed so far. If you need the framework to do some heavy-duty work, you can extend the SQL mapper by declaring your own classes with custom methods - but you can't avoid getting your hands greasy on some hardcore SQL:-

``` php
class Vendor extends DB\SQL\Mapper {

    // Instantiate mapper
    function __construct(DB\SQL $db) {
        // This is where the mapper and DB structure synchronization occurs
        parent::__construct($db,'vendors');
    }

    // Specialized query
    function listByCity() {
        return $this->select(
            'vendorID,name,city',['order'=>'city DESC']);
        /*
            We could have done the the same thing with plain vanilla SQL:-
            return $this->db->exec(
                'SELECT vendorID,name,city FROM vendors '.
                'ORDER BY city DESC;'
            );
        */
    }

}

$vendor=new Vendor;
$vendor->listByCity();
```

Extending the data mappers in this fashion is an easy way to construct your application's DB-related models.

### Pros and Cons

If you're handy with SQL, you'd probably say: everything in the ORM can be handled with old-school SQL queries. Indeed. We can do without the additional event listeners by using database triggers and stored procedures. We can accomplish relational queries with joined tables. The ORM is just unnecessary overhead. But the point is - data mappers give you the added functionality of using objects to represent database entities. As a developer, you can write code faster and be more productive. The resulting program will be cleaner, if not shorter. But you'll have to weigh the benefits against the compromise in speed - specially when handling large and complex data stores. Remember, all ORMS - no matter how thin they are - will always be just another abstraction layer. They still have to pass the work to the underlying SQL engines.

By design, F3's ORMs do not provide methods for directly connecting objects to each other, i.e. SQL joins - because this opens up a can of worms. It makes your application more complex than it should be, and there's the tendency of objects thru eager or lazy fetching techniques to be deadlocked and even out of sync due to object inheritance and polymorphism (impedance mismatch) with the database entities they're mapped to. There are indirect ways of doing it in the SQL mapper, using virtual fields - but you'll have to do this programmatically and at your own risk.

If you are tempted to apply "pure" OOP concepts in your application to represent all your data (because "everything is an object"), keep in mind that data almost always lives longer than the application. Your program may already be outdated long before the data has lost its value. Don't add another layer of complexity in your program by using intertwined objects and classes that deviate too much from the schema and physical structure of the data.

Before you weave multiple objects together in your application to manipulate the underlying tables in your database, think about this: creating views to represent relationships and triggers to define object behavior in the database engine are more efficient. Relational database engines are designed to handle views, joined tables and triggers. They are not dumb data stores. Tables joined in a view will appear as a single table, and Fat-Free can auto-map a view just as well as a regular table. Replicating JOINs as relational objects in PHP is slower compared to the database engine's machine code, relational algebra and optimization logic. Besides, joining tables repeatedly in our application is a sure sign that the database design needs to be audited, and views considered an integral part of data retrieval. If a table cross-references data from another table frequently, consider normalizing your structures or creating a view instead. Then create a mapper object to auto-map that view. It's faster and requires less effort.

Consider this SQL view created inside your database engine:-

``` sql
CREATE VIEW combined AS
    SELECT
        projects.project_id AS project,
        users.name AS name
    FROM projects
    LEFT OUTER JOIN users ON
        projects.project_id=users.project_id AND
        projects.user_id=users.user_id;
```

Your application code becomes simple because it does not have to maintain two mapper objects (one for the projects table and another for users) just to retrieve data from two joined tables:-

``` php
$combined=new DB\SQL\Mapper($db,'combined');
$combined->load(['project=?',123]);
echo $combined->name;
```

Tip:Use the tools as they're designed for. Fat-Free already has an easy-to-use SQL helper. Use it if you need a bigger hammer :) Try to seek a balance between convenience and performance. SQL will always be your fallback if you're working on complex and legacy data structures.

## Plug-Ins

### About F3 Plug-ins

Plug-ins are nothing more than autoloaded classes that use framework built-ins to extend F3's features and functionality. If you'd like to contribute, leave a note at the Fat-Free Discussion Area hosted by Google Groups or tell us about it in the FreeNode `#fatfree` IRC channel. Someone else might be involved in a similar project. The framework community will appreciate it a lot if we unify our efforts.

### CAPTCHA Images

There might be instances when you want to make your forms more secure against spam bots and malicious automated scripts. F3 provides a `captcha()` method to generate images with random text that are designed to be recognizable only by humans.

``` php
$img = new Image();
$img->captcha('fonts/CoolFont.ttf',16,5,'SESSION.captcha_code');
$img->render();
```

This example generates an random image based on your desired TrueType font. The `fonts/` folder is a subfolder within application's `UI` path. The second parameter indicates the font size, and the third argument defines the number of hexadecimal characters to generate.

The last argument represents an F3 variable name. This is where F3 will store the string equivalent of the CAPTCHA image. To make the string reload-safe, we specified a session variable:- `SESSION.captcha_code` which maps to `$_SESSION['captcha_code']`, which you can use later to verify whether the input element in the form submitted matches this string.

### Grabbing Data from Another Site

We've covered almost every feature available in the framework to run a stand-alone Web server. For most applications, these features will serve you quite well. But what do you do if your application needs data from another Web server on the network? F3 has the Web plugin to help you in this situation:-

``` php
$web=new Web;
$request=$web->request('http://www.google.com/');
// another way to do it:-
$request=Web::instance()->request('http://www.google.com/');
```

This simple example sends an HTTP request to the page located at www.google.com and stores it in the `$request` PHP variable. The `request()` method returns an array containing the HTTP response such that `$request['headers']` and `$request['body']` represent the response headers and body, respectively. We could have saved the contents using the F3::set command, or echo'ed the output directly to our browser. Retrieving another HTML page on the net may not have any practical purpose. But it can be particularly useful in ReSTful applications, like querying a CouchDB server.

``` php
$host='localhost:5984';
$web->request($host.'/_all_dbs'),
$web->request($host.'/testdb/',['method'=>'PUT']);
```

You may have noticed that you can pass an array of additional options to the `request()` method:-

``` php
$web->request(
    'https://www.example.com:443?'.
    http_build_query(
        [
            'key1'=>'value1',
            'key2'=>'value2'
        ]
    ),
    [
        'headers'=>[
            'Accept: text/html,application/xhtml+xml,application/xml',
            'Accept-Language: en-us'
        ],
        'follow_location'=>FALSE,
        'max_redirects'=>30,
        'ignore_errors'=>TRUE
    ]
);
```

If the framework variable `CACHE` is enabled, and if the remote server instructs your application to cache the response to the HTTP request, F3 will comply with the request and retrieve the cached response each time the framework receives a similar request from your application, thus behaving like a browser.

Fat-Free will use whatever means are available on your Web server for the `request()` method to run: PHP stream wrappers (`allow_url_fopen`), cURL module, or low-level sockets.

### Handling File Downloads

F3 has a utility for sending files to an HTTP client, i.e. fulfilling download requests. You can use it to hide the real path to your download files. This adds some layer of security because users won't be able to download files if they don't know the file names and their locations. Here's how it's done:-

``` php
$f3->route('GET /downloads/@filename',
    function($f3,$args) {
        // send() method returns FALSE if file doesn't exist
        if (!Web::instance()->send('/real/path/'.$args['filename']))
            // Generate an HTTP 404
        $f3->error(404);
    }
);
```

### Remoting and Distributed Applications

The `request()` method can also be used in complex SOAP or XML-RPC applications, if you find the need for another Web server to process data on your computer's behalf - thus harnessing the power of distributing computing. W3Schools.com has an excellent tutorial on SOAP. On the other hand, TutorialsPoint.com gives a nice overview of XML-RPC.

## Optimization

### Cache Engine

Caching static Web pages - so the code in some route handlers can be skipped and templates don't have to be reprocessed - is one way of reducing your Web server's work load so it can focus on other tasks. You can activate the framework's cache engine by providing a third argument to the `$f3->route()` method. Just specify the number of seconds before a cached Web page expires:-

``` php
$f3->route('GET /my_page','App->method',60);
```

Here's how it works. In this example, when F3 detects that the URL `/my_page` is accessed for the first time, it executes the route handler represented by the second argument and saves all browser output to the framework's built-in cache (server-side). A similar instruction is automatically sent to the user's Web browser (client-side), so that instead of sending an identical request to the server within the 60-second period, the browser can just retrieve the page locally. The framework uses the cache for an entirely different purpose - serving framework-cached data to other users asking for the same Web page within the 60-second time frame. It skips execution of the route handler and serves the previously-saved page directly from disk. When someone tries to access the same URL after the 60-second timer has lapsed, F3 will refresh the cache with a new copy.

Web pages with static data are the most likely candidates for caching. Fat-Free will not cache a Web page at a specified URL if the third argument in the `$f3->route()` method is zero or unspecified. F3 conforms to the HTTP specifications: only GET and HEAD requests can be cached.

Here's an important point to consider when designing your application. Don't cache Web pages unless you understand the possible unwanted side-effects of the cache at the client-side. Make sure that you activate caching on Web pages that have nothing to do with the user's session state.

For example, you designed your site in such a way that all your Web pages have the menu options: `"Home"`, `"About Us"`, and `"Login"`, displayed when a user is not logged into your application. You also want the menu options to change to: `"Home"`, `"About Us"`, and `"Logout"`, once the user has logged in. If you instructed Fat-Free to cache the contents of `"About Us"` page (which includes the menu options), it does so and also sends the same instruction to the HTTP client. Regardless of the user's session state, i.e. logged in or logged out, the user's browser will take a snapshot of the page at the session state it was in. Future requests by the user for the `"About Us"` page before the cache timeout expires will display the same menu options available at that time the page was initially saved. Now, a user may have already logged in, but the menu options are still the same as if no such event occurred. That's not the kind of behavior we want from our application.

Some pointers:-

* Don't cache dynamic pages. It's quite obvious you don't want to cache data that changes frequently. You can, however, activate caching on pages that contain data updated on an hourly, daily or even yearly basis.For security reasons, the framework restricts cache engine usage to HTTP `GET` routes only. It will not cache submitted forms!Don't activate the cache on Web pages that at first glance look static. In our example, the "About Us" content may be static, but the menu isn't.
* Activate caching on pages that are available only in ONE session state. If you want to cache the `"About Us"` page, make sure it's available only when a user is not logged in.
* If you have a RAMdisk or fast solid-state drive, configure the `CACHE` global variable so it points to that drive. This will make your application run like a Formula 1 race car.

**Note:** Don't set the timeout value to a very long period until you're ready to roll out your application, i.e. the release or production state. Changes you make to any of your PHP scripts may not have the expected effect on the displayed output if the page exists in the framework cache and the expiration period has not lapsed. If you do alter a program that generates a page affected by the cache timer and you want these changes to take effect immediately, you should clear the cache by erasing the files in the cache/ directory (or whatever path the `CACHE` global variable points to). F3 will automatically refresh the cache if necessary. At the client-side, there's little you can do but instruct the user to clear the browser's cache or wait for the cache period to expire.

PHP needs to be set up correctly for the F3 cache engine to work properly. Your operating system timezone should be synchronized with the date.timezone setting in the `php.ini` file.

Similar to routes, Fat-Free also allows you to cache database queries. Speed gains can be quite significant, specially when used on complex SQL statements that involve look-up of static data or database content that rarely changes. Activating the database query cache so the framework doesn't have to re-execute the SQL statements every time is as simple as adding a 3rd argument to the F3::sql command - the cache timeout. For example:-

``` php
$db->exec('SELECT * from sizes;',NULL,86400);
```

If we expect the result of this database query to always be `Small`, `Medium`, and `Large` within a 24-hour period, we specify `86400` seconds as the 2nd argument so Fat-Free doesn't have to execute the query more than once a day. Instead, the framework will store the result in the cache, retrieve it from the cache every time a request comes in during the specified 24-hour time frame, and re-execute the query when the timer lapses.

The SQL data mapper also uses the cache engine to optimize synchronization of table structures with the objects that represent them. The default is `60` seconds. If you make any changes to a table's structure in your database engine, you'll have to wait for the cache timer to expire before seeing the effect in your application. You can change this behavior by specifying a third argument to the data mapper constructor. Set it to a high value if you don't expect to make any further changes to your table structure.

``` php
$user=new DB\SQL\Mapper($db,'users',86400);
```

By default, Fat-Free's cache engine is disabled. You can enable it and allow it to auto-detect APC, WinCache or XCache. If it cannot find an appropriate backend, F3 will use the filesystem, i.e. the `tmp/cache/` folder:-

``` php
$f3->set('CACHE',TRUE);
```

Disabling the cache is as simple as:-

``` php
$f3->set('CACHE',FALSE);
```

If you wish to override the auto-detection feature, you can do so - as in the case of a Memcached back-end which F3 also supports:-

``` php
$f3->set('CACHE','memcache=localhost:11211');
```

You can also use the cache engine to store your own variables. These variables will persist between HTTP requests and remain in cache until the engine receives instructions to delete them. To save a value in the cache:-

``` php
$f3->set('var','I want this value saved',90);
```

`$f3->set()` method's third argument instructs the framework to save the variable in the cache for a 90-second duration. If your application issues a `$f3->get('var')` within this period, F3 will automatically retrieve the value from cache. In like manner, `$f3->clear('var')` will purge the value from both cache and RAM. If you want to determine if a variable exists in cache, `$f3->exists('var')); returns one of two possible values: FALSE if the framework variable passed does not exist in cache, or an integer representing the time the variable was saved (Un*x time in seconds, with microsecond precision).

### Keeping Javascript and CSS on a Healthy Diet

Fat-Free also has a Javascript and CSS compressor available in the Web plug-in. It can combine all your CSS files into one stylesheet (or Javascript files into a single script) so the number of components on a Web page are decreased. Reducing the number of HTTP requests to your Web server results in faster page loading. First you need to prepare your HTML template so it can take advantage of this feature. Something like:-

``` html
<link rel="stylesheet" type="text/css"
	href="/minify/css?files=typo.css,grid.css" />
```

Do the same with your Javascript files:-

``` html
<script type="text/javascript" src="/minify/js?&files=underscore.js">
</script>
```

Of course we need to set up a route so your application can handle the necessary call to the Fat-Free CSS/Javascript compressor:-

``` php
$f3->route('GET /minify/@type',
    function($f3,$args) {
        $f3->set('UI',$args['type'].'/');
        echo Web::instance()->minify($_GET['files']);
    },
    3600
);
```

And that's all there is to it! `minify()` reads each file (`typo.css` and `grid.css` in our CSS example, `underscore.js` in our Javascript example), strips off all unnecessary whitespaces and comments, combines all of the related items as a single Web page component, and attaches a far-future expiry date so the user's Web browser can cache the data. It's important that the `PARAMS.type` variable base points to the correct path. Otherwise, the URL rewriting mechanism inside the compressor won't find the CSS/Javascript files.

### Client-Side Caching

In our examples, the framework sends a far-future expiry date to the client's Web browser so any request for the same CSS or Javascript block will come from the user's hard drive. On the server side, F3 will check each request and see if the CSS or Javascript blocks have already been cached. The route we specified has a cache refresh period of `3600` seconds. Additionally, if the Web browser sends an `If-Modified-Since` request header and the framework sees the cache hasn't changed, F3 just sends an `HTTP 304 Not Modified` response so no content is actually delivered. Without the `If-Modified-Since` header, Fat-Free renders the output from the cached file if available. Otherwise, the relevant code is executed.

Tip: If you're not modifying your Javascript/CSS files frequently (as it would be if you're using a Javascript library like jQuery, MooTools, Dojo, etc.), consider adding a cache timer to the route leading to your Javascript/CSS minify handler (3rd argument of F3::route()) so Fat-Free doesn't have compress and combine these files each time such a request is received.

### PHP Code Acceleration

Want to make your site run even faster? Fat-Free works best with either Alternative PHP Cache (APC), XCache, or WinCache. These PHP extensions boost performance of your application by optimizing your PHP scripts (including the framework code).

### Bandwidth Throttling

A fast application that processes all HTTP requests and responds to them at the shortest time possible is not always a good idea - specially if your bandwidth is limited or traffic on your Web site is particularly heavy. Serving pages ASAP also makes your application vulnerable to Denial-of-Service (DOS) attacks. F3 has a bandwidth throttling feature that allows you to control how fast your Web pages are served. You can specify how much time it should take to process a request:-

``` php
$f3->route('/throttledpage','MyApp->handler',0,128);
```

In this example, the framework will serve the Web page at a rate of 128KiBps.

Bandwidth throttling at the application level can be particularly useful for login pages. Slow responses to dictionary attacks is a good way of mitigating this kind of security risk.

## Unit Testing

### Bullet-Proof Code

Robust applications are the result of comprehensive testing. Verifying that each part of your program conforms to the specifications and lives up to the expectations of the end-user means finding bugs and fixing them as early as possible in the application development cycle.

If you know little or nothing about unit testing methodologies, you're probably embedding pieces of code directly in your existing program to help you with debugging. That of course means you have to remove them once the program is running. Leftover code fragments, poor design and faulty implementation can creep up as bugs when you roll out your application later.

F3 makes it easy for you to debug programs - without getting in the way of your regular thought processes. The framework does not require you to build complex OOP classes, heavy test structures, and obtrusive procedures.

A unit (or test fixture) can be a function/method or a class. Let's have a simple example:-

``` php
function hello() {
    return 'Hello, World';
}
```

Save it in a file called `hello.php`. Now how do we know it really runs as expected? Let's create our test procedure:-

``` php
$f3=require('lib/base.php');

// Set up
$test=new Test;
include('hello.php');

// This is where the tests begin
$test->expect(
    is_callable('hello'),
    'hello() is a function'
);

// Another test
$hello=hello();
$test->expect(
    !empty($hello),
    'Something was returned'
);

// This test should succeed
$test->expect
    is_string($hello),
    'Return value is a string'
);

// This test is bound to fail
$test->expect(
    strlen($hello)==13,
    'String length is 13'
);

// Display the results; not MVC but let's keep it simple
foreach ($test->results() as $result) {
    echo $result['text'].'<br />';
    if ($result['status'])
        echo 'Pass';
    else
        echo 'Fail ('.$result['source'].')';
    echo '<br />';
}
```

Save it in a file called `test.php`. This way we can preserve the integrity of `hello.php`.

Now here's the meat of our unit testing process.

F3's built-in `Test` class keeps track of the result of each `expect()` call. The output of `$test->results()` is an array of arrays with the keys `text` (mirroring argument 2 of `expect()`), `status` (boolean representing the result of a test), and `source` (file name/line number of the specific test) to aid in debugging.

Fat-Free gives you the freedom to display test results in any way you want. You can have the output in plain text or even a nice-looking HTML template. So how do we run our unit test? If you saved `test.php` in the document root folder, you can just open your browser and specify the address `http://localhost/test.php`. That's all there is to it.

### Mocking HTTP Requests

F3 gives you the ability to simulate HTTP requests from within your PHP program so you can test the behavior of a particular route. Here's a simple mock request:-

``` php
$f3->mock('GET /test?foo=bar');
```

To mock a POST request and submit a simulated HTML form:-

``` php
$f3->mock('POST /test',['foo'=>'bar']);
```

### Expecting the Worst that can Happen

Once you get the hang of testing the smallest units of your application, you can then move on to the bigger components, modules, and subsystems - checking along the way if the parts are correctly communicating with each other. Testing manageable chunks of code leads to more reliable programs that work as you expect, and weaves the testing process into the fabric of your development cycle. The question to ask yourself is:- Have I tested all possible scenarios? More often than not, those situations that have not been taken into consideration are the likely causes of bugs. Unit testing helps a lot in minimizing these occurrences. Even a few tests on each fixture can greatly reduce headaches. On the other hand, writing applications without unit testing at all invites trouble.

## Quick Reference

### System Variables

`string AGENT`

* Auto-detected HTTP user agent, e.g. `Mozilla/5.0 (Linux; Android 4.2.2; Nexus 7) AppleWebKit/537.31`.

`bool AJAX`

* `TRUE` if an XML HTTP request is detected, `FALSE` otherwise.

`string AUTOLOAD`

* Search path for user-defined PHP classes that the framework will attempt to autoload at runtime. Accepts a pipe (`|`), comma (`,`), or semi-colon (`;`) as path separator.

`string BASE`

* Path to the `index.php` main/front controller.

`string BODY`

* HTTP request body for ReSTful post-processing.

`bool/string CACHE`

* Cache backend. Unless assigned a value like `'memcache=localhost'` (and the PHP memcache module is present), F3 auto-detects the presence of APC, WinCache and XCache and uses the first available PHP module if set to TRUE. If none of these PHP modules are available, a filesystem-based backend is used (default directory: `tmp/cache`). The framework disables the cache engine if assigned a `FALSE` value.

`bool CASELESS`

* Pattern matching of routes against incoming URIs is case-insensitive by default. Set to `FALSE` to make it case-sensitive.

`array COOKIE, GET, POST, REQUEST, SESSION, FILES, SERVER, ENV`

* Framework equivalents of PHP globals. Variables may be used throughout an application. However, direct use in templates is not advised due to security risks.

`integer DEBUG`

* Stack trace verbosity. Assign values 1 to 3 for increasing verbosity levels. Zero (0) suppresses the stack trace. This is the default value and it should be the assigned setting on a production server.

`string DNSBL`

* Comma-separated list of [DNS blacklist servers]http://whatismyipaddress.com/blacklist-check. Framework generates a `403 Forbidden` error if the user's IPv4 address is listed on the specified server(s).

`array DIACRITICS`

* Key-value pairs for foreign-to-ASCII character translations.

`string ENCODING`

* Character set used for document encoding. Default value is `UTF-8`.

`array ERROR`

* Information about the last HTTP error that occurred. `ERROR.code` is the HTTP status code. `ERROR.status` contains a brief description of the error. `ERROR.text` provides more detail. For HTTP 500 errors, use `ERROR.trace` to retrieve the stack trace.

`bool ESCAPE`

* Used to enable/disable auto-escaping.

`string EXEMPT`

* Comma-separated list of IPv4 addresses exempt from DNSBL lookups.

`string FALLBACK`

* Language (and dictionary) to use if no translation is available.

`bool HALT`

* If TRUE (default), framework stops execution after a non-fatal error is detected.

`array HEADERS`

* HTTP request headers received by the server.

`bool HIGHLIGHT`

* Enable/disable syntax highlighting of stack traces. Default value: `TRUE` (requires `code.css` stylesheet).

`string HOST`

* Server host name. If `$_SERVER['SERVER_NAME']` is not available, return value of `gethostname()` is used.

`string IP`

* Remote IP address. The framework derives the address from headers if HTTP client is behind a proxy server.

`array JAR`

* Default cookie parameters.

`string LANGUAGE`

* Current active language. Value is used to load the appropriate language translation file in the folder pointed to by `LOCALES`. If set to `NULL`, language is auto-detected from the HTTP `Accept-Language` request header.

`string LOCALES`

* Location of the language dictionaries.

`string LOGS`

* Location of custom logs.

`mixed ONERROR`

* Callback function to use as custom error handler.

`string PACKAGE`

* Framework name.

`array PARAMS`

* Captured values of tokens defined in a `route()` pattern. `PARAMS.0` contains the captured URL relative to the Web root.

`string PATTERN`

* Contains the routing pattern that matches the current request URI.

`string PLUGINS`

* Location of F3 plugins. Default value is the folder where the framework code resides, i.e. the path to `base.php`.

`int PORT`

* TCP/IP listening port used by the Web server.

`string PREFIX`

* String prepended to language dictionary terms.

`bool QUIET`

* Toggle switch for suppressing or enabling standard output and error messages. Particularly useful in unit testing.

`bool RAW`

* Disable automatic storage of HTTP request body into `BODY`. Should be TRUE when processing large data coming from `php://input` which will not fit in memory. Default value: `FALSE`

`string REALM`

* Full canonical URL.

`string RESPONSE`

* The body of the last HTTP response. F3 populates this variable regardless of the `QUIET` setting.

`string ROOT`

* Absolute path to document root folder.

`array ROUTES`

* Contains the defined application routes.

`string SCHEME`

* Server protocol, i.e. `http` or `https`.

`string SERIALIZER`

* Default serializer. Normally set to `php`, unless PHP `igbinary` extension is auto-detected. Assign `json` if desired.

`string TEMP`

* Temporary folder for cache, filesystem locks, compiled F3 templates, etc. Default is the `tmp/` folder inside the Web root. Adjust accordingly to conform to your site's security policies.

`string TZ`

* Default timezone. Changing this value automatically calls the underlying `date_default_timezone_set()` function.

`string UI`

* Search path for user interface files used by the `View` and `Template` classes' `render()` method. Default value is the Web root. Accepts a pipe (`|`), comma (`,`), or semi-colon (`;`) as separator for multiple paths.

`callback UNLOAD`

* Executed by framework on script shutdown.

`string UPLOADS`

* Directory where file uploads are saved.

`string URI`

* Current HTTP request URI.

`string VERB`

* Current HTTP request method.

`string VERSION`

* Framework version.

### Template Directives

```
@token
```
* Replace `@token` with value of equivalent F3 variable.

```
{{ mixed expr }}
```
* Evaluate. `expr` may include template tokens, constants, operators (unary, arithmetic, ternary and relational), parentheses, data type converters, and functions. If not an attribute of a template directive, result is echoed.

```
{{ string expr | raw }}
```
* Render unescaped `expr`. F3 auto-escapes strings by default.

```
{{ string expr | esc }}
```
* Render escaped `expr`. This is the default framework behavior. The `| esc` suffix is only necessary if `ESCAPE` global variable is set to `FALSE`.

```
{{ string expr, arg1, ..., argN | format }}
```
* Render an ICU-formatted `expr` and pass the comma-separated arguments, where `arg1, ..., argn` is one of:- `'date'`, `'time'`, `'number, integer'`, `'number, currency'`, or `'number, percent'`.

```
<include
    [ if="{{ bool condition }}" ]
    href="{{ string subtemplate }}"
/>
```
* Get contents of `subtemplate` and insert at current position in template if optional condition is `TRUE`.

```
<exclude>text-block</exclude>
```
* Remove `text-block` at runtime. Used for embedding comments in templates.

```
<ignore>text-block</ignore>
```
* Display `text-block` as-is, without interpretation/modification by the template engine.

```
<check if="{{ bool condition }}">
    <true>true-block</true>
    <false>false-block</false>
</check>
```
* Evaluate condition. If `TRUE`, then `true-block` is rendered. Otherwise, `false-block` is used.

```
<loop
    from="{{ statement }}"
    to="{{ bool expr }}"
    [ step="{{ statement }}" ]>
    text-block
</loop>
```
* Evaluate `from` statement once. Check if the expression in the `to` attribute is `TRUE`, render `text-block` and evaluate `step` statement. Repeat iteration until `to` expression is `FALSE`.

```
<repeat
    group="{{ array @group|expr }}"
    [ key="{{ scalar @key }}" ]
    value="{{ mixed @value }}"
    [ counter="{{ scalar @key }}" ]>
    text-block
</repeat>
```
* Repeat `text-block` as many times as there are elements in the array variable `@group` or the expression `expr`. `@key` and `@value` function in the same manner as the key-value pair in the equivalent PHP `foreach()` statement. Variable represented by `key` in `counter` attribute increments by `1` with every iteration.

```
<switch expr="{{ scalar expr }}">
    <case value="{{ scalar @value|expr }}" break="{{ bool TRUE|FALSE }}">
        text-block
    </case>
    .
    .
    .
</switch>
```
* Equivalent of the PHP switch-case jump table structure.

```
{* text-block *}
```
* Alias for `<exclude>`.

### API Documentation

The most up-to-date documentation is located at [http://fatfreeframework.com/]http://fatfreeframework.com/. It contains examples of usage of the various framework components.

## Support and Licensing

Technical support is available at the official discussion forum: [`https://groups.google.com/forum/#!forum/f3-framework`]https://groups.google.com/forum/#!forum/f3-framework. If you need live support, you can talk to the development team and other members of the F3 community via IRC. We're on the FreeNode `#fatfree` channel (`chat.freenode.net`). Visit [`http://webchat.freenode.net/`]http://webchat.freenode.net/ to join the conversation. You can also download the [Firefox Chatzilla]https://addons.mozilla.org/en-US/firefox/addon/chatzilla/ add-on or [Pidgin]http://www.pidgin.im/ if you don't have an IRC client so you can participate in the live chat.

### Nightly Builds

F3 uses Git for version control. To clone the code repository on GitHub:-

``` bash
git clone git://github.com/bcosca/fatfree.git
```

If all you want is a zipball, grab it [**here**]https://github.com/bcosca/fatfree/archive/dev.zip.

To file a bug report, visit [`https://github.com/bcosca/fatfree/issues`]https://github.com/bcosca/fatfree/issues.

### Fair Licensing

**Fat-Free Framework is free and released as open source software covered by the terms of the [GNU Public License]http://www.gnu.org/licenses/gpl-3.0.html (GPL v3).** You may not use the software, documentation, and samples except in compliance with the license. If the terms and conditions of this license are too restrictive for your use, alternative licensing is available for a very reasonable fee.

If you feel that this software is one great weapon to have in your programming arsenal, it saves you a lot of time and money, use it for commercial gain or in your business organization, please consider making a donation to the project. A significant amount of time, effort, and money has been spent on this project. Your donations help keep this project alive and the development team motivated. Donors and sponsors get priority support (24-hour response time on business days).

### Credits

The Fat-Free Framework is community-driven software. It can't be what it is today without the help and support from the following people and organizations:

* GitHub
* Stehlik & Company
* bodalgo.com
* Square Lines, LLC
* Mirosystems
* Talis Group, Ltd.
* Tecnilógica
* G Holdings, LLC
* S2 Development, Ltd.
* Store Machine
* PHP Experts, Inc.
* Meins und Vogel GmbH
* Online Prepaid Services
* Frugal Photographer
* Christian Knuth
* Florent Racineux
* Sascha Ohms
* Lars Brandi Jensen
* Eyðun Lamhauge
* Jermaine Maree
* Sergey Zaretsky
* Daniel Kloke
* Brian Nelson
* Roberts Lapins
* Boris Gurevich
* Jose Maria Garrido Diaz
* Dawn Comfort
* Johan Viberg
* Povilas Musteikis
* Andrew Snook
* Jafar Amjad
* Taylor McCall
* Raymond Kirkland
* Yuriy Gerassimenko
* William Stam
* Sam George
* Steve Wasiura
* Andreas Ljunggren
* Sashank Tadepalli
* Chad Bishop
* Bradley Slavik
* Lee Blue
* Alexander Shatilo
* Justin Noel
* Ivan Kovac
* Tony's Internet Solutions
* Charles Stigler
* Attila van der Velde
* Indoblo Commerce Ltd.
* Jens Níemeyer
* Raghu Veer Dendukuri
* NovelLead B.V.
* Emir Alp
* Dominic Schwarz
* Sven Zahrend
* LucidStorm
* Nevatech
* Matt Wielgos
* Maximilian Summe
* Caspar Frey
* FocusHeart
* Philip Lawrence
* Peter Beverwyk
* Judith Grass
* Randal Hintz
* Franz Josef
* Biswajit Nayak
* R Mohan
* Michael Messner
* Jason Borseth
* Dmitrij Chernov
* Marek Toman
* Simone Cociancich
* Alan Holding
* Philipp Hirsch
* Aurélien Botermans
* Christian Treptow
* Кубарев Дмитрий (Dmitry Kubarev)
* Alexandru Catalin Trandafir
* Leigh Harrison
* Дмитриев Иван (Ivan Dmitriev)
* IT_GAP
* Sergeev Andrey
* Steven J Mixon
* Roland Fath
* Justin Parker
* Costas Menico
* Mathieu-Philippe Bourgeois
* Ryan McKillop
* Chris Clarke
* Ngan Ting On
* Eli Argon
* Seregin Andrew
* Marek Toman
* Diji Enterprises
* uonick
* Kamil Kiblis
* Mars Yau
* Martin Latinov
* Malikov Evgene
* Andres Espinoza Arce
* Matthew Williamson
* Andrew Brookes
* Steve Cove
* Steven Witten
* Silvan Seeholzer
* Toni Schönbuchner
* Marek Toman
* Dexter Freivald
* Chad West
* Bond Akinmade
* AlpiSol - Ernaldo Pisati
* Adam Wilkins
* Mihai Flaviu Molnar
* Carolina R Molla
* Andres Espinoza Arce
* Jan Kremlacek
* Eric Schultz
* Ricardo Andrade
* Derek Loewen
* Michael Nelson
* Denis Bach
* Lenard Osmani

Special thanks to the selfless others who expressed their desire to remain anonymous, yet share their time, contribute code, send donations, promote the framework to a wider audience, as well as provide encouragement and regular financial assistance. Their generosity is F3's prime motivation.

[![Paypal]ui/images/paypal.png]https://www.paypal.me/fatfree

![Bitcoin]ui/images/bitcoin.png

### Legal notice

By making a donation to this project you signify that you acknowledged, understood, accepted, and agreed to the terms and conditions contained in this notice. Your donation to the Fat-Free Framework project is voluntary and is not a fee for any services, goods, or advantages, and making a donation to the project does not entitle you to any services, goods, or advantages. We have the right to use the money you donate to the Fat-Free Framework project in any lawful way and for any lawful purpose we see fit and we are not obligated to disclose the way and purpose to any party unless required by applicable law. Although Fat-Free Framework is free software, to our best knowledge this project does not have any tax-exempt status. The Fat-Free Framework project is neither a registered non-profit corporation nor a registered charity in any country. Your donation may or may not be tax-deductible; please consult this with your tax advisor. We will not publish/disclose your name and e-mail address without your consent, unless required by applicable law. Your donation is non-refundable.

**Copyright (c) 2009-2017 F3::Factory/Bong Cosca &lt;bong&#46;cosca&#64;yahoo&#46;com&gt;**
diff --git a/vendor/fatfree-3.6/ui/css/base.css b/vendor/fatfree-3.6/ui/css/base.css
new file mode 100755
index 0000000..1cfee5a
--- /dev/null
+++ b/vendor/fatfree-3.6/ui/css/base.css
@@ -0,0 +1,6 @@
/* Reset */
html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,figcaption,figure,footer,header,hgroup,dir,menu,nav,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}nav ul{list-style:none}ol{list-style:decimal}ul{list-style:disc}ul ul{list-style:circle}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}a{margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent}ins{text-decoration:underline}mark{background:none}del{text-decoration:line-through}abbr[title],dfn[title]{border-bottom:1px dotted;cursor:help}table{border-collapse:collapse;border-spacing:0}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}input,select,a img{vertical-align:middle}
/* Typography */
*{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;max-width:100%}html{height:100%;font-size:100%;font-family:serif;overflow-y:scroll;-webkit-text-size-adjust:100%}body{margin:0;min-height:100%;overflow:hidden}body,pre,label,input,button,select,textarea{font:normal 100%/1.25 serif;vertical-align:top}a{display:inline-block}p,ul,ol{margin:1.25em 0}h1{font-size:2em;line-height:1.25em;margin:0.625em 0}h2{font-size:1.5em;line-height:1.6667em;margin:0.8333em 0}h3{font-size:1.25em;line-height:1em;margin:1em 0}h4{font-size:1em;line-height:1.25em;margin:1.25em 0}h5{font-size:0.8125em;line-height:1.5385em;margin:1.5385em 0}h6{font-size:0.6875em;line-height:1.8182em;margin:1.8182em 0}blockquote{margin:0 3em}caption{font-weight:bold}ul,ol,dir,menu,dd{margin-left:3em}ul,dir,menu{list-style:disc}ol{list-style:decimal}sub,sup{font-size:75%;line-height:0;vertical-align:baseline;position:relative}sub{top:0.5em}sup{top:-0.5em}label{display:inline-block}input[type="text"],input[type="password"],input[type="file"]{padding:1px;border:1px solid #999;margin:-4px 0 0 0}select,textarea{padding:0;border:1px solid #999;margin:-4px 0 0 0}fieldset{padding:0.625em;border:1px solid #ccc;margin-bottom:0.625em}input[type="radio"],input[type="checkbox"]{height:1em;vertical-align:top;margin:0.125em}div,table{overflow:hidden}
/* Fluid Fonts */
@media screen and (max-width:960px){body{font-size:0.81255em}}
\ No newline at end of file
diff --git a/vendor/fatfree-3.6/ui/css/theme.css b/vendor/fatfree-3.6/ui/css/theme.css
new file mode 100755
index 0000000..aee0fe9
--- /dev/null
+++ b/vendor/fatfree-3.6/ui/css/theme.css
@@ -0,0 +1,165 @@
body {
	font-family:Ubuntu,sans-serif;
	font-size:1.2em;
}

h1 {
	color:#faa;
}

h2 {
	color:#e88;
}

h3 {
	color:#b66;
}

h4 {
	color:#944;
}

h5 {
	color:#722;
}

h6 {
	color:#500;
}

small {
	font-size:0.75em
}

a {
	text-decoration:none;
	color:#66f;
}

a:hover {
	color:#666;
}

img {
	max-width:100%
}

table {
	font-size:.8em;
	color:#666 !important;
	background:#eee;
	width:100%;
	border-radius:.5em;
}

table code {
	background:transparent;
	padding:0
}

tr {
	border-bottom:1px solid #fff;
}

tr:last-child {
	border-bottom:none;
}

th,td {
	font-size:1em;
	line-height:1.25em;
	margin:0;
	padding:1em;
	white-space:nowrap;
}

th {
	font-weight:bold;
	text-align:left;
	text-transform:uppercase;
	color:#fff;
	background:#999;
}

th a {
	color:#fff;
}

th:first-child,
td:first-child {
	width:50%;
}

pre {
	background:#efefef;
	padding:0.75em;
	border-radius:0.75em;
}

ul,p {
	color:#666;
	line-height:1.5em;
}

p code,ul code {
	padding:.25em .75em;
	border-radius:.75em;
	white-space:nowrap
}

blockquote pre,blockquote code {
	color:#666;
	background:#fff;
}

code {
	background:#eee;
}

.center {
	text-align:center;
}

.right {
	text-align:right;
}

.content {
	padding:0 20px;
	max-width:768px;
	margin:0 auto;
}

.header {
	background:#eee;
}

.header img {
	width:90%;
	max-width:768px;
	padding:0 5%;
}

.footer {
	font-size:0.9em;
	background:#333;
}

.footer p {
	color:#eee;
	padding:20px;
	max-width:768px;
	margin:0 auto;
}

.footer .stats {
	font-size:.9em;
}

@media screen and (max-width:48em) {

	body {
		font-size:1em;
	}

}
diff --git a/vendor/fatfree-3.6/ui/images/bitcoin.png b/vendor/fatfree-3.6/ui/images/bitcoin.png
new file mode 100755
index 0000000..06c597d
Binary files /dev/null and b/vendor/fatfree-3.6/ui/images/bitcoin.png differ
diff --git a/vendor/fatfree-3.6/ui/images/logo.png b/vendor/fatfree-3.6/ui/images/logo.png
new file mode 100755
index 0000000..0667487
Binary files /dev/null and b/vendor/fatfree-3.6/ui/images/logo.png differ
diff --git a/vendor/fatfree-3.6/ui/images/paypal.png b/vendor/fatfree-3.6/ui/images/paypal.png
new file mode 100755
index 0000000..80e89f2
Binary files /dev/null and b/vendor/fatfree-3.6/ui/images/paypal.png differ
diff --git a/vendor/fatfree-3.6/ui/images/twitter.png b/vendor/fatfree-3.6/ui/images/twitter.png
new file mode 100755
index 0000000..c1b1a8d
Binary files /dev/null and b/vendor/fatfree-3.6/ui/images/twitter.png differ
diff --git a/vendor/fatfree-3.6/ui/layout.htm b/vendor/fatfree-3.6/ui/layout.htm
new file mode 100755
index 0000000..fb76133
--- /dev/null
+++ b/vendor/fatfree-3.6/ui/layout.htm
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="<?php echo $ENCODING; ?>" />
		<title>Powered by <?php echo $PACKAGE; ?></title>
		<base href="<?php echo $SCHEME.'://'.$HOST.':'.$PORT.$BASE.'/'; ?>" />
		<link rel="stylesheet" href="lib/code.css" type="text/css" />
		<link rel="stylesheet" href="ui/css/base.css" type="text/css" />
		<link rel="stylesheet" href="ui/css/theme.css" type="text/css" />
	</head>
	<body>
		<?php echo $this->render(Base::instance()->get('content')); ?>
	</body>
</html>
diff --git a/vendor/fatfree-3.6/ui/userref.htm b/vendor/fatfree-3.6/ui/userref.htm
new file mode 100755
index 0000000..5b740ad
--- /dev/null
+++ b/vendor/fatfree-3.6/ui/userref.htm
@@ -0,0 +1,4 @@
<div class="content">
	<?php echo Markdown::instance()->
		convert(Base::instance()->read('readme.md')); ?>
</div>
diff --git a/vendor/fatfree-3.6/ui/welcome.htm b/vendor/fatfree-3.6/ui/welcome.htm
new file mode 100755
index 0000000..341d856
--- /dev/null
+++ b/vendor/fatfree-3.6/ui/welcome.htm
@@ -0,0 +1,61 @@
<div class="header center">
	<p><img src="ui/images/logo.png"></p>
</div>
<div class="content">
	<h2>Version <?php echo $VERSION; ?></h2>
	<p>The first thing you might want to do is visualize your directory structures. Fat-Free gives you total control over your Web site. Organize your folders in any way that pleases you (or your development team if you're part of a group). Decide where you want to store the following:</p>
	<ul>
		<li>Application and code libraries</li>
		<li>HTML templates</li>
		<li>Graphics and media files</li>
		<li>Javascript and CSS files</li>
		<li>Database (if you plan to use an embedded DB like SQLite)</li>
		<li>Configuration files</li>
		<li>Uploads/Downloads</li>
	</ul>
	<p>For security reasons, consider relocating the <code>lib/</code> folder to a path that's not Web-accessible. If you decide to move this folder, just change the line in <code>index.php</code> containing <code>require 'lib/base.php';</code> so it points to the new location. The <code>lib/</code> folder also contains framework plug-ins that extend F3's capabilities. You can change the default location of all plug-ins by moving the files to your desired subdirectory. Then, it's just a matter of pointing the <code>PLUGINS</code> global variable to the new location. You may delete the plug-ins that you don't need. You can reinstate them later as you find necessary.</p>
	<p>F3 can autoload OOP classes for you. Just add the path to the <code>AUTOLOAD</code> variable.</p>
	<p>When you're ready to write your F3-enabled site, you can start editing the rest of the code contained in the <code>index.php</code> file that displayed this Web page. Developing PHP applications will never be the same!</p>
	<h2>PHP Dependencies</h2>
	<p>Some framework features in this version will not be available if PHP is not configured with the modules needed by your application.</p>
	<table>
		<tr>
			<th>Class/Plug-in</th>
			<th>PHP Module</th>
		</tr>
		<?php foreach ($classes as $class=>$modules): ?>
		<tr>
			<td><?php echo $class; ?></td>
			<td>
				<?php foreach ($modules as $module): ?>
				<input type="checkbox" <?php echo extension_loaded($module)?'checked':''?> onclick="return false"> <?php echo $module; ?><br>
				<?php endforeach; ?>
			</td>
		</tr>
		<?php endforeach; ?>
	</table>
	<ul>
	<li>The <code>Base</code> class requires all listed PHP modules enabled to function properly.</li>
	<li>The <code>Cache</code> class will use any available module in the list. If none can be found, it will use the filesystem as fallback.</li>
	<li>The <code>DB\SQL</code> class requires the <code>pdo</code> module and a PDO driver relevant to your application.</li>
	<li>The <code>Bcrypt</code> class will use the <code>mcrypt</code> or <code>openssl</code> module for entropy generation. Otherwise, it employs a custom random function.</li>
	<li>The <code>Web</code> class will use the <code>curl</code> module for HTTP requests to another server. If this is not detected, it will use other transports available, such as the HTTP stream wrapper or native sockets.</li>
	<li>The <code>geoip</code> module listed in the <code>Web\Geo</code> class is optional; the class will use an alternative Web service for geo-location.</li>
	<li>Other framework classes in the list need all its listed modules enabled.</li>
	</ul>
	<h2>Need Help?</h2>
	<p>If you have any questions regarding the framework, technical support is available at <code><a href="https://groups.google.com/forum/?fromgroups#!forum/f3-framework">https://groups.google.com/forum/?fromgroups#!forum/f3-framework</a></code></p>
	<p>Need live support? You can talk to the development team and the rest of the Fat-Free community via IRC. We're on the FreeNode (<code>chat.freenode.net</code>) <code>#fatfree</code> channel. If the channel appears quiet, the development team might just be busy with the next great release, or it's probably due to time zone differences. Just hang around.</p>
	<p>The <strong><a href="<?php echo $BASE; ?>/userref">User Reference</a></strong> is designed to serve as a handbook and programming guide. However, the online documentation at <a href="https://github.com/bcosca/fatfree" onclick="window.open(this.href); return false;"><code>https://github.com/bcosca/fatfree</code></a> provides the latest and most comprehensive information about the framework.</p>
	<h2>Fair Licensing</h2>
	<p><b>Fat-Free Framework is free software covered by the terms of the GNU Public License (GPL v3).</b> You may not use the software, documentation, and samples except in compliance with the license. If the terms and conditions of this license are too restrictive for your use, alternative licensing is available for a very reasonable fee.</p>
	<p>If you feel that this software is one great weapon to have in your programming arsenal, it saves you a lot of time and money, use it for commercial gain or in your business organization, please consider making a donation to the project. A significant amount of time, effort, and money has been spent on this project. Your donations help keep this project alive and the development team motivated. Donors and sponsors get priority support commensurate to your contribution (24-hour response time on business days).</p>
	<h2>Support F3</h2>
	<p>F3 is community-driven software. Support the development of the Fat-Free Framework. Your contributions help keep this project alive.</p>
	<p><a href="bitcoin:18cJLaAYT5mmu72GeoMgajbKFcmCZEjK12" target="_blank"><img src="ui/images/bitcoin.png"></a></p>
</div>
<div class="footer center">
	<p>Fat-Free Framework is licensed under the terms of the GPL, either v3 or later<br>
	Copyright &copy; 2009-2017 F3::Factory/Bong Cosca &lt;bong&#46;cosca&#64;yahoo&#46;com&gt;</p>
	<p class="stats"><code><?php echo Base::instance()->format('Page rendered in {0} msecs / Memory usage {1} Kibytes',round(1e3*(microtime(TRUE)-$TIME),2),round(memory_get_usage(TRUE)/1e3,1)); ?></code></p>
</div>
diff --git a/vendor/parsedown/LICENSE.txt b/vendor/parsedown/LICENSE.txt
new file mode 100755
index 0000000..e6253d1
--- /dev/null
+++ b/vendor/parsedown/LICENSE.txt
@@ -0,0 +1,20 @@
The MIT License (MIT)

Copyright (c) 2013-2018 Emanuil Rusev, erusev.com

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/parsedown/parsedown.php b/vendor/parsedown/parsedown.php
new file mode 100755
index 0000000..2ce8eb5
--- /dev/null
+++ b/vendor/parsedown/parsedown.php
@@ -0,0 +1,1966 @@
<?php

#
#
# Parsedown
# http://parsedown.org
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, view the LICENSE file that was distributed
# with this source code.
#
#

class Parsedown
{
    # ~

    const version = '1.8.0-beta-4';

    # ~

    function text($text)
    {
        $Elements = $this->textElements($text);

        # convert to markup
        $markup = $this->elements($Elements);

        # trim line breaks
        $markup = trim($markup, "\n");

        return $markup;
    }

    protected function textElements($text)
    {
        # make sure no definitions are set
        $this->DefinitionData = array();

        # standardize line breaks
        $text = str_replace(array("\r\n", "\r"), "\n", $text);

        # remove surrounding line breaks
        $text = trim($text, "\n");

        # split text into lines
        $lines = explode("\n", $text);

        # iterate through lines to identify blocks
        return $this->linesElements($lines);
    }

    #
    # Setters
    #

    function setBreaksEnabled($breaksEnabled)
    {
        $this->breaksEnabled = $breaksEnabled;

        return $this;
    }

    protected $breaksEnabled;

    function setMarkupEscaped($markupEscaped)
    {
        $this->markupEscaped = $markupEscaped;

        return $this;
    }

    protected $markupEscaped;

    function setUrlsLinked($urlsLinked)
    {
        $this->urlsLinked = $urlsLinked;

        return $this;
    }

    protected $urlsLinked = true;

    function setSafeMode($safeMode)
    {
        $this->safeMode = (bool) $safeMode;

        return $this;
    }

    protected $safeMode;

    function setStrictMode($strictMode)
    {
        $this->strictMode = (bool) $strictMode;

        return $this;
    }

    protected $strictMode;

    protected $safeLinksWhitelist = array(
        'http://',
        'https://',
        'ftp://',
        'ftps://',
        'mailto:',
        'data:image/png;base64,',
        'data:image/gif;base64,',
        'data:image/jpeg;base64,',
        'irc:',
        'ircs:',
        'git:',
        'ssh:',
        'news:',
        'steam:',
    );

    #
    # Lines
    #

    protected $BlockTypes = array(
        '#' => array('Header'),
        '*' => array('Rule', 'List'),
        '+' => array('List'),
        '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
        '0' => array('List'),
        '1' => array('List'),
        '2' => array('List'),
        '3' => array('List'),
        '4' => array('List'),
        '5' => array('List'),
        '6' => array('List'),
        '7' => array('List'),
        '8' => array('List'),
        '9' => array('List'),
        ':' => array('Table'),
        '<' => array('Comment', 'Markup'),
        '=' => array('SetextHeader'),
        '>' => array('Quote'),
        '[' => array('Reference'),
        '_' => array('Rule'),
        '`' => array('FencedCode'),
        '|' => array('Table'),
        '~' => array('FencedCode'),
    );

    # ~

    protected $unmarkedBlockTypes = array(
        'Code',
    );

    #
    # Blocks
    #

    protected function lines(array $lines)
    {
        return $this->elements($this->linesElements($lines));
    }

    protected function linesElements(array $lines)
    {
        $Elements = array();
        $CurrentBlock = null;

        foreach ($lines as $line)
        {
            if (chop($line) === '')
            {
                if (isset($CurrentBlock))
                {
                    $CurrentBlock['interrupted'] = (isset($CurrentBlock['interrupted'])
                        ? $CurrentBlock['interrupted'] + 1 : 1
                    );
                }

                continue;
            }

            while (($beforeTab = strstr($line, "\t", true)) !== false)
            {
                $shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4;

                $line = $beforeTab
                    . str_repeat(' ', $shortage)
                    . substr($line, strlen($beforeTab) + 1)
                ;
            }

            $indent = strspn($line, ' ');

            $text = $indent > 0 ? substr($line, $indent) : $line;

            # ~

            $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);

            # ~

            if (isset($CurrentBlock['continuable']))
            {
                $methodName = 'block' . $CurrentBlock['type'] . 'Continue';
                $Block = $this->$methodName($Line, $CurrentBlock);

                if (isset($Block))
                {
                    $CurrentBlock = $Block;

                    continue;
                }
                else
                {
                    if ($this->isBlockCompletable($CurrentBlock['type']))
                    {
                        $methodName = 'block' . $CurrentBlock['type'] . 'Complete';
                        $CurrentBlock = $this->$methodName($CurrentBlock);
                    }
                }
            }

            # ~

            $marker = $text[0];

            # ~

            $blockTypes = $this->unmarkedBlockTypes;

            if (isset($this->BlockTypes[$marker]))
            {
                foreach ($this->BlockTypes[$marker] as $blockType)
                {
                    $blockTypes []= $blockType;
                }
            }

            #
            # ~

            foreach ($blockTypes as $blockType)
            {
                $Block = $this->{"block$blockType"}($Line, $CurrentBlock);

                if (isset($Block))
                {
                    $Block['type'] = $blockType;

                    if ( ! isset($Block['identified']))
                    {
                        if (isset($CurrentBlock))
                        {
                            $Elements[] = $this->extractElement($CurrentBlock);
                        }

                        $Block['identified'] = true;
                    }

                    if ($this->isBlockContinuable($blockType))
                    {
                        $Block['continuable'] = true;
                    }

                    $CurrentBlock = $Block;

                    continue 2;
                }
            }

            # ~

            if (isset($CurrentBlock) and $CurrentBlock['type'] === 'Paragraph')
            {
                $Block = $this->paragraphContinue($Line, $CurrentBlock);
            }

            if (isset($Block))
            {
                $CurrentBlock = $Block;
            }
            else
            {
                if (isset($CurrentBlock))
                {
                    $Elements[] = $this->extractElement($CurrentBlock);
                }

                $CurrentBlock = $this->paragraph($Line);

                $CurrentBlock['identified'] = true;
            }
        }

        # ~

        if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
        {
            $methodName = 'block' . $CurrentBlock['type'] . 'Complete';
            $CurrentBlock = $this->$methodName($CurrentBlock);
        }

        # ~

        if (isset($CurrentBlock))
        {
            $Elements[] = $this->extractElement($CurrentBlock);
        }

        # ~

        return $Elements;
    }

    protected function extractElement(array $Component)
    {
        if ( ! isset($Component['element']))
        {
            if (isset($Component['markup']))
            {
                $Component['element'] = array('rawHtml' => $Component['markup']);
            }
            elseif (isset($Component['hidden']))
            {
                $Component['element'] = array();
            }
        }

        return $Component['element'];
    }

    protected function isBlockContinuable($Type)
    {
        return method_exists($this, 'block' . $Type . 'Continue');
    }

    protected function isBlockCompletable($Type)
    {
        return method_exists($this, 'block' . $Type . 'Complete');
    }

    #
    # Code

    protected function blockCode($Line, $Block = null)
    {
        if (isset($Block) and $Block['type'] === 'Paragraph' and ! isset($Block['interrupted']))
        {
            return;
        }

        if ($Line['indent'] >= 4)
        {
            $text = substr($Line['body'], 4);

            $Block = array(
                'element' => array(
                    'name' => 'pre',
                    'element' => array(
                        'name' => 'code',
                        'text' => $text,
                    ),
                ),
            );

            return $Block;
        }
    }

    protected function blockCodeContinue($Line, $Block)
    {
        if ($Line['indent'] >= 4)
        {
            if (isset($Block['interrupted']))
            {
                $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);

                unset($Block['interrupted']);
            }

            $Block['element']['element']['text'] .= "\n";

            $text = substr($Line['body'], 4);

            $Block['element']['element']['text'] .= $text;

            return $Block;
        }
    }

    #
    # Comment

    protected function blockComment($Line)
    {
        if ($this->markupEscaped or $this->safeMode)
        {
            return;
        }

        if (strpos($Line['text'], '<!--') === 0)
        {
            $Block = array(
                'element' => array(
                    'rawHtml' => $Line['body'],
                    'autobreak' => true,
                ),
            );

            if (strpos($Line['text'], '-->') !== false)
            {
                $Block['closed'] = true;
            }

            return $Block;
        }
    }

    protected function blockCommentContinue($Line, array $Block)
    {
        if (isset($Block['closed']))
        {
            return;
        }

        $Block['element']['rawHtml'] .= "\n" . $Line['body'];

        if (strpos($Line['text'], '-->') !== false)
        {
            $Block['closed'] = true;
        }

        return $Block;
    }

    #
    # Fenced Code

    protected function blockFencedCode($Line)
    {
        $marker = $Line['text'][0];

        $openerLength = strspn($Line['text'], $marker);

        if ($openerLength < 3)
        {
            return;
        }

        $infostring = trim(substr($Line['text'], $openerLength), "\t ");

        if (strpos($infostring, '`') !== false)
        {
            return;
        }

        $Element = array(
            'name' => 'code',
            'text' => '',
        );

        if ($infostring !== '')
        {
            $Element['attributes'] = array('class' => "language-$infostring");
        }

        $Block = array(
            'char' => $marker,
            'openerLength' => $openerLength,
            'element' => array(
                'name' => 'pre',
                'element' => $Element,
            ),
        );

        return $Block;
    }

    protected function blockFencedCodeContinue($Line, $Block)
    {
        if (isset($Block['complete']))
        {
            return;
        }

        if (isset($Block['interrupted']))
        {
            $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);

            unset($Block['interrupted']);
        }

        if (($len = strspn($Line['text'], $Block['char'])) >= $Block['openerLength']
            and chop(substr($Line['text'], $len), ' ') === ''
        ) {
            $Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1);

            $Block['complete'] = true;

            return $Block;
        }

        $Block['element']['element']['text'] .= "\n" . $Line['body'];

        return $Block;
    }

    #
    # Header

    protected function blockHeader($Line)
    {
        $level = strspn($Line['text'], '#');

        if ($level > 6)
        {
            return;
        }

        $text = trim($Line['text'], '#');

        if ($this->strictMode and isset($text[0]) and $text[0] !== ' ')
        {
            return;
        }

        $text = trim($text, ' ');

        $Block = array(
            'element' => array(
                'name' => 'h' . min(6, $level),
                'handler' => array(
                    'function' => 'lineElements',
                    'argument' => $text,
                    'destination' => 'elements',
                )
            ),
        );

        return $Block;
    }

    #
    # List

    protected function blockList($Line, array $CurrentBlock = null)
    {
        list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]');

        if (preg_match('/^('.$pattern.'([ ]++|$))(.*+)/', $Line['text'], $matches))
        {
            $contentIndent = strlen($matches[2]);

            if ($contentIndent >= 5)
            {
                $contentIndent -= 1;
                $matches[1] = substr($matches[1], 0, -$contentIndent);
                $matches[3] = str_repeat(' ', $contentIndent) . $matches[3];
            }
            elseif ($contentIndent === 0)
            {
                $matches[1] .= ' ';
            }

            $markerWithoutWhitespace = strstr($matches[1], ' ', true);

            $Block = array(
                'indent' => $Line['indent'],
                'pattern' => $pattern,
                'data' => array(
                    'type' => $name,
                    'marker' => $matches[1],
                    'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : substr($markerWithoutWhitespace, -1)),
                ),
                'element' => array(
                    'name' => $name,
                    'elements' => array(),
                ),
            );
            $Block['data']['markerTypeRegex'] = preg_quote($Block['data']['markerType'], '/');

            if ($name === 'ol')
            {
                $listStart = ltrim(strstr($matches[1], $Block['data']['markerType'], true), '0') ?: '0';

                if ($listStart !== '1')
                {
                    if (
                        isset($CurrentBlock)
                        and $CurrentBlock['type'] === 'Paragraph'
                        and ! isset($CurrentBlock['interrupted'])
                    ) {
                        return;
                    }

                    $Block['element']['attributes'] = array('start' => $listStart);
                }
            }

            $Block['li'] = array(
                'name' => 'li',
                'handler' => array(
                    'function' => 'li',
                    'argument' => !empty($matches[3]) ? array($matches[3]) : array(),
                    'destination' => 'elements'
                )
            );

            $Block['element']['elements'] []= & $Block['li'];

            return $Block;
        }
    }

    protected function blockListContinue($Line, array $Block)
    {
        if (isset($Block['interrupted']) and empty($Block['li']['handler']['argument']))
        {
            return null;
        }

        $requiredIndent = ($Block['indent'] + strlen($Block['data']['marker']));

        if ($Line['indent'] < $requiredIndent
            and (
                (
                    $Block['data']['type'] === 'ol'
                    and preg_match('/^[0-9]++'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
                ) or (
                    $Block['data']['type'] === 'ul'
                    and preg_match('/^'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
                )
            )
        ) {
            if (isset($Block['interrupted']))
            {
                $Block['li']['handler']['argument'] []= '';

                $Block['loose'] = true;

                unset($Block['interrupted']);
            }

            unset($Block['li']);

            $text = isset($matches[1]) ? $matches[1] : '';

            $Block['indent'] = $Line['indent'];

            $Block['li'] = array(
                'name' => 'li',
                'handler' => array(
                    'function' => 'li',
                    'argument' => array($text),
                    'destination' => 'elements'
                )
            );

            $Block['element']['elements'] []= & $Block['li'];

            return $Block;
        }
        elseif ($Line['indent'] < $requiredIndent and $this->blockList($Line))
        {
            return null;
        }

        if ($Line['text'][0] === '[' and $this->blockReference($Line))
        {
            return $Block;
        }

        if ($Line['indent'] >= $requiredIndent)
        {
            if (isset($Block['interrupted']))
            {
                $Block['li']['handler']['argument'] []= '';

                $Block['loose'] = true;

                unset($Block['interrupted']);
            }

            $text = substr($Line['body'], $requiredIndent);

            $Block['li']['handler']['argument'] []= $text;

            return $Block;
        }

        if ( ! isset($Block['interrupted']))
        {
            $text = preg_replace('/^[ ]{0,'.$requiredIndent.'}+/', '', $Line['body']);

            $Block['li']['handler']['argument'] []= $text;

            return $Block;
        }
    }

    protected function blockListComplete(array $Block)
    {
        if (isset($Block['loose']))
        {
            foreach ($Block['element']['elements'] as &$li)
            {
                if (end($li['handler']['argument']) !== '')
                {
                    $li['handler']['argument'] []= '';
                }
            }
        }

        return $Block;
    }

    #
    # Quote

    protected function blockQuote($Line)
    {
        if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
        {
            $Block = array(
                'element' => array(
                    'name' => 'blockquote',
                    'handler' => array(
                        'function' => 'linesElements',
                        'argument' => (array) $matches[1],
                        'destination' => 'elements',
                    )
                ),
            );

            return $Block;
        }
    }

    protected function blockQuoteContinue($Line, array $Block)
    {
        if (isset($Block['interrupted']))
        {
            return;
        }

        if ($Line['text'][0] === '>' and preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
        {
            $Block['element']['handler']['argument'] []= $matches[1];

            return $Block;
        }

        if ( ! isset($Block['interrupted']))
        {
            $Block['element']['handler']['argument'] []= $Line['text'];

            return $Block;
        }
    }

    #
    # Rule

    protected function blockRule($Line)
    {
        $marker = $Line['text'][0];

        if (substr_count($Line['text'], $marker) >= 3 and chop($Line['text'], " $marker") === '')
        {
            $Block = array(
                'element' => array(
                    'name' => 'hr',
                ),
            );

            return $Block;
        }
    }

    #
    # Setext

    protected function blockSetextHeader($Line, array $Block = null)
    {
        if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
        {
            return;
        }

        if ($Line['indent'] < 4 and chop(chop($Line['text'], ' '), $Line['text'][0]) === '')
        {
            $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';

            return $Block;
        }
    }

    #
    # Markup

    protected function blockMarkup($Line)
    {
        if ($this->markupEscaped or $this->safeMode)
        {
            return;
        }

        if (preg_match('/^<[\/]?+(\w*)(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+(\/)?>/', $Line['text'], $matches))
        {
            $element = strtolower($matches[1]);

            if (in_array($element, $this->textLevelElements))
            {
                return;
            }

            $Block = array(
                'name' => $matches[1],
                'element' => array(
                    'rawHtml' => $Line['text'],
                    'autobreak' => true,
                ),
            );

            return $Block;
        }
    }

    protected function blockMarkupContinue($Line, array $Block)
    {
        if (isset($Block['closed']) or isset($Block['interrupted']))
        {
            return;
        }

        $Block['element']['rawHtml'] .= "\n" . $Line['body'];

        return $Block;
    }

    #
    # Reference

    protected function blockReference($Line)
    {
        if (strpos($Line['text'], ']') !== false
            and preg_match('/^\[(.+?)\]:[ ]*+<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches)
        ) {
            $id = strtolower($matches[1]);

            $Data = array(
                'url' => $matches[2],
                'title' => isset($matches[3]) ? $matches[3] : null,
            );

            $this->DefinitionData['Reference'][$id] = $Data;

            $Block = array(
                'element' => array(),
            );

            return $Block;
        }
    }

    #
    # Table

    protected function blockTable($Line, array $Block = null)
    {
        if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
        {
            return;
        }

        if (
            strpos($Block['element']['handler']['argument'], '|') === false
            and strpos($Line['text'], '|') === false
            and strpos($Line['text'], ':') === false
            or strpos($Block['element']['handler']['argument'], "\n") !== false
        ) {
            return;
        }

        if (chop($Line['text'], ' -:|') !== '')
        {
            return;
        }

        $alignments = array();

        $divider = $Line['text'];

        $divider = trim($divider);
        $divider = trim($divider, '|');

        $dividerCells = explode('|', $divider);

        foreach ($dividerCells as $dividerCell)
        {
            $dividerCell = trim($dividerCell);

            if ($dividerCell === '')
            {
                return;
            }

            $alignment = null;

            if ($dividerCell[0] === ':')
            {
                $alignment = 'left';
            }

            if (substr($dividerCell, - 1) === ':')
            {
                $alignment = $alignment === 'left' ? 'center' : 'right';
            }

            $alignments []= $alignment;
        }

        # ~

        $HeaderElements = array();

        $header = $Block['element']['handler']['argument'];

        $header = trim($header);
        $header = trim($header, '|');

        $headerCells = explode('|', $header);

        if (count($headerCells) !== count($alignments))
        {
            return;
        }

        foreach ($headerCells as $index => $headerCell)
        {
            $headerCell = trim($headerCell);

            $HeaderElement = array(
                'name' => 'th',
                'handler' => array(
                    'function' => 'lineElements',
                    'argument' => $headerCell,
                    'destination' => 'elements',
                )
            );

            if (isset($alignments[$index]))
            {
                $alignment = $alignments[$index];

                $HeaderElement['attributes'] = array(
                    'style' => "text-align: $alignment;",
                );
            }

            $HeaderElements []= $HeaderElement;
        }

        # ~

        $Block = array(
            'alignments' => $alignments,
            'identified' => true,
            'element' => array(
                'name' => 'table',
                'elements' => array(),
            ),
        );

        $Block['element']['elements'] []= array(
            'name' => 'thead',
        );

        $Block['element']['elements'] []= array(
            'name' => 'tbody',
            'elements' => array(),
        );

        $Block['element']['elements'][0]['elements'] []= array(
            'name' => 'tr',
            'elements' => $HeaderElements,
        );

        return $Block;
    }

    protected function blockTableContinue($Line, array $Block)
    {
        if (isset($Block['interrupted']))
        {
            return;
        }

        if (count($Block['alignments']) === 1 or $Line['text'][0] === '|' or strpos($Line['text'], '|'))
        {
            $Elements = array();

            $row = $Line['text'];

            $row = trim($row);
            $row = trim($row, '|');

            preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches);

            $cells = array_slice($matches[0], 0, count($Block['alignments']));

            foreach ($cells as $index => $cell)
            {
                $cell = trim($cell);

                $Element = array(
                    'name' => 'td',
                    'handler' => array(
                        'function' => 'lineElements',
                        'argument' => $cell,
                        'destination' => 'elements',
                    )
                );

                if (isset($Block['alignments'][$index]))
                {
                    $Element['attributes'] = array(
                        'style' => 'text-align: ' . $Block['alignments'][$index] . ';',
                    );
                }

                $Elements []= $Element;
            }

            $Element = array(
                'name' => 'tr',
                'elements' => $Elements,
            );

            $Block['element']['elements'][1]['elements'] []= $Element;

            return $Block;
        }
    }

    #
    # ~
    #

    protected function paragraph($Line)
    {
        return array(
            'type' => 'Paragraph',
            'element' => array(
                'name' => 'p',
                'handler' => array(
                    'function' => 'lineElements',
                    'argument' => $Line['text'],
                    'destination' => 'elements',
                ),
            ),
        );
    }

    protected function paragraphContinue($Line, array $Block)
    {
        if (isset($Block['interrupted']))
        {
            return;
        }

        $Block['element']['handler']['argument'] .= "\n".$Line['text'];

        return $Block;
    }

    #
    # Inline Elements
    #

    protected $InlineTypes = array(
        '!' => array('Image'),
        '&' => array('SpecialCharacter'),
        '*' => array('Emphasis'),
        ':' => array('Url'),
        '<' => array('UrlTag', 'EmailTag', 'Markup'),
        '[' => array('Link'),
        '_' => array('Emphasis'),
        '`' => array('Code'),
        '~' => array('Strikethrough'),
        '\\' => array('EscapeSequence'),
    );

    # ~

    protected $inlineMarkerList = '!*_&[:<`~\\';

    #
    # ~
    #

    public function line($text, $nonNestables = array())
    {
        return $this->elements($this->lineElements($text, $nonNestables));
    }

    protected function lineElements($text, $nonNestables = array())
    {
        $Elements = array();

        $nonNestables = (empty($nonNestables)
            ? array()
            : array_combine($nonNestables, $nonNestables)
        );

        # $excerpt is based on the first occurrence of a marker

        while ($excerpt = strpbrk($text, $this->inlineMarkerList))
        {
            $marker = $excerpt[0];

            $markerPosition = strlen($text) - strlen($excerpt);

            $Excerpt = array('text' => $excerpt, 'context' => $text);

            foreach ($this->InlineTypes[$marker] as $inlineType)
            {
                # check to see if the current inline type is nestable in the current context

                if (isset($nonNestables[$inlineType]))
                {
                    continue;
                }

                $Inline = $this->{"inline$inlineType"}($Excerpt);

                if ( ! isset($Inline))
                {
                    continue;
                }

                # makes sure that the inline belongs to "our" marker

                if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
                {
                    continue;
                }

                # sets a default inline position

                if ( ! isset($Inline['position']))
                {
                    $Inline['position'] = $markerPosition;
                }

                # cause the new element to 'inherit' our non nestables


                $Inline['element']['nonNestables'] = isset($Inline['element']['nonNestables'])
                    ? array_merge($Inline['element']['nonNestables'], $nonNestables)
                    : $nonNestables
                ;

                # the text that comes before the inline
                $unmarkedText = substr($text, 0, $Inline['position']);

                # compile the unmarked text
                $InlineText = $this->inlineText($unmarkedText);
                $Elements[] = $InlineText['element'];

                # compile the inline
                $Elements[] = $this->extractElement($Inline);

                # remove the examined text
                $text = substr($text, $Inline['position'] + $Inline['extent']);

                continue 2;
            }

            # the marker does not belong to an inline

            $unmarkedText = substr($text, 0, $markerPosition + 1);

            $InlineText = $this->inlineText($unmarkedText);
            $Elements[] = $InlineText['element'];

            $text = substr($text, $markerPosition + 1);
        }

        $InlineText = $this->inlineText($text);
        $Elements[] = $InlineText['element'];

        foreach ($Elements as &$Element)
        {
            if ( ! isset($Element['autobreak']))
            {
                $Element['autobreak'] = false;
            }
        }

        return $Elements;
    }

    #
    # ~
    #

    protected function inlineText($text)
    {
        $Inline = array(
            'extent' => strlen($text),
            'element' => array(),
        );

        $Inline['element']['elements'] = self::pregReplaceElements(
            $this->breaksEnabled ? '/[ ]*+\n/' : '/(?:[ ]*+\\\\|[ ]{2,}+)\n/',
            array(
                array('name' => 'br'),
                array('text' => "\n"),
            ),
            $text
        );

        return $Inline;
    }

    protected function inlineCode($Excerpt)
    {
        $marker = $Excerpt['text'][0];

        if (preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(?<!['.$marker.'])\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
        {
            $text = $matches[2];
            $text = preg_replace('/[ ]*+\n/', ' ', $text);

            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'code',
                    'text' => $text,
                ),
            );
        }
    }

    protected function inlineEmailTag($Excerpt)
    {
        $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?';

        $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@'
            . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*';

        if (strpos($Excerpt['text'], '>') !== false
            and preg_match("/^<((mailto:)?$commonMarkEmail)>/i", $Excerpt['text'], $matches)
        ){
            $url = $matches[1];

            if ( ! isset($matches[2]))
            {
                $url = "mailto:$url";
            }

            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'a',
                    'text' => $matches[1],
                    'attributes' => array(
                        'href' => $url,
                    ),
                ),
            );
        }
    }

    protected function inlineEmphasis($Excerpt)
    {
        if ( ! isset($Excerpt['text'][1]))
        {
            return;
        }

        $marker = $Excerpt['text'][0];

        if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
        {
            $emphasis = 'strong';
        }
        elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
        {
            $emphasis = 'em';
        }
        else
        {
            return;
        }

        return array(
            'extent' => strlen($matches[0]),
            'element' => array(
                'name' => $emphasis,
                'handler' => array(
                    'function' => 'lineElements',
                    'argument' => $matches[1],
                    'destination' => 'elements',
                )
            ),
        );
    }

    protected function inlineEscapeSequence($Excerpt)
    {
        if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
        {
            return array(
                'element' => array('rawHtml' => $Excerpt['text'][1]),
                'extent' => 2,
            );
        }
    }

    protected function inlineImage($Excerpt)
    {
        if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
        {
            return;
        }

        $Excerpt['text']= substr($Excerpt['text'], 1);

        $Link = $this->inlineLink($Excerpt);

        if ($Link === null)
        {
            return;
        }

        $Inline = array(
            'extent' => $Link['extent'] + 1,
            'element' => array(
                'name' => 'img',
                'attributes' => array(
                    'src' => $Link['element']['attributes']['href'],
                    'alt' => $Link['element']['handler']['argument'],
                ),
                'autobreak' => true,
            ),
        );

        $Inline['element']['attributes'] += $Link['element']['attributes'];

        unset($Inline['element']['attributes']['href']);

        return $Inline;
    }

    protected function inlineLink($Excerpt)
    {
        $Element = array(
            'name' => 'a',
            'handler' => array(
                'function' => 'lineElements',
                'argument' => null,
                'destination' => 'elements',
            ),
            'nonNestables' => array('Url', 'Link'),
            'attributes' => array(
                'href' => null,
                'title' => null,
            ),
        );

        $extent = 0;

        $remainder = $Excerpt['text'];

        if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
        {
            $Element['handler']['argument'] = $matches[1];

            $extent += strlen($matches[0]);

            $remainder = substr($remainder, $extent);
        }
        else
        {
            return;
        }

        if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
        {
            $Element['attributes']['href'] = $matches[1];

            if (isset($matches[2]))
            {
                $Element['attributes']['title'] = substr($matches[2], 1, - 1);
            }

            $extent += strlen($matches[0]);
        }
        else
        {
            if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
            {
                $definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument'];
                $definition = strtolower($definition);

                $extent += strlen($matches[0]);
            }
            else
            {
                $definition = strtolower($Element['handler']['argument']);
            }

            if ( ! isset($this->DefinitionData['Reference'][$definition]))
            {
                return;
            }

            $Definition = $this->DefinitionData['Reference'][$definition];

            $Element['attributes']['href'] = $Definition['url'];
            $Element['attributes']['title'] = $Definition['title'];
        }

        return array(
            'extent' => $extent,
            'element' => $Element,
        );
    }

    protected function inlineMarkup($Excerpt)
    {
        if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
        {
            return;
        }

        if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches))
        {
            return array(
                'element' => array('rawHtml' => $matches[0]),
                'extent' => strlen($matches[0]),
            );
        }

        if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?+[^-])*-->/s', $Excerpt['text'], $matches))
        {
            return array(
                'element' => array('rawHtml' => $matches[0]),
                'extent' => strlen($matches[0]),
            );
        }

        if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches))
        {
            return array(
                'element' => array('rawHtml' => $matches[0]),
                'extent' => strlen($matches[0]),
            );
        }
    }

    protected function inlineSpecialCharacter($Excerpt)
    {
        if ($Excerpt['text'][1] !== ' ' and strpos($Excerpt['text'], ';') !== false
            and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches)
        ) {
            return array(
                'element' => array('rawHtml' => '&' . $matches[1] . ';'),
                'extent' => strlen($matches[0]),
            );
        }

        return;
    }

    protected function inlineStrikethrough($Excerpt)
    {
        if ( ! isset($Excerpt['text'][1]))
        {
            return;
        }

        if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
        {
            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'del',
                    'handler' => array(
                        'function' => 'lineElements',
                        'argument' => $matches[1],
                        'destination' => 'elements',
                    )
                ),
            );
        }
    }

    protected function inlineUrl($Excerpt)
    {
        if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
        {
            return;
        }

        if (strpos($Excerpt['context'], 'http') !== false
            and preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)
        ) {
            $url = $matches[0][0];

            $Inline = array(
                'extent' => strlen($matches[0][0]),
                'position' => $matches[0][1],
                'element' => array(
                    'name' => 'a',
                    'text' => $url,
                    'attributes' => array(
                        'href' => $url,
                    ),
                ),
            );

            return $Inline;
        }
    }

    protected function inlineUrlTag($Excerpt)
    {
        if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches))
        {
            $url = $matches[1];

            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'a',
                    'text' => $url,
                    'attributes' => array(
                        'href' => $url,
                    ),
                ),
            );
        }
    }

    # ~

    protected function unmarkedText($text)
    {
        $Inline = $this->inlineText($text);
        return $this->element($Inline['element']);
    }

    #
    # Handlers
    #

    protected function handle(array $Element)
    {
        if (isset($Element['handler']))
        {
            if (!isset($Element['nonNestables']))
            {
                $Element['nonNestables'] = array();
            }

            if (is_string($Element['handler']))
            {
                $function = $Element['handler'];
                $argument = $Element['text'];
                unset($Element['text']);
                $destination = 'rawHtml';
            }
            else
            {
                $function = $Element['handler']['function'];
                $argument = $Element['handler']['argument'];
                $destination = $Element['handler']['destination'];
            }

            $Element[$destination] = $this->{$function}($argument, $Element['nonNestables']);

            if ($destination === 'handler')
            {
                $Element = $this->handle($Element);
            }

            unset($Element['handler']);
        }

        return $Element;
    }

    protected function handleElementRecursive(array $Element)
    {
        return $this->elementApplyRecursive(array($this, 'handle'), $Element);
    }

    protected function handleElementsRecursive(array $Elements)
    {
        return $this->elementsApplyRecursive(array($this, 'handle'), $Elements);
    }

    protected function elementApplyRecursive($closure, array $Element)
    {
        $Element = call_user_func($closure, $Element);

        if (isset($Element['elements']))
        {
            $Element['elements'] = $this->elementsApplyRecursive($closure, $Element['elements']);
        }
        elseif (isset($Element['element']))
        {
            $Element['element'] = $this->elementApplyRecursive($closure, $Element['element']);
        }

        return $Element;
    }

    protected function elementApplyRecursiveDepthFirst($closure, array $Element)
    {
        if (isset($Element['elements']))
        {
            $Element['elements'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['elements']);
        }
        elseif (isset($Element['element']))
        {
            $Element['element'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['element']);
        }

        $Element = call_user_func($closure, $Element);

        return $Element;
    }

    protected function elementsApplyRecursive($closure, array $Elements)
    {
        foreach ($Elements as &$Element)
        {
            $Element = $this->elementApplyRecursive($closure, $Element);
        }

        return $Elements;
    }

    protected function elementsApplyRecursiveDepthFirst($closure, array $Elements)
    {
        foreach ($Elements as &$Element)
        {
            $Element = $this->elementApplyRecursiveDepthFirst($closure, $Element);
        }

        return $Elements;
    }

    protected function element(array $Element)
    {
        if ($this->safeMode)
        {
            $Element = $this->sanitiseElement($Element);
        }

        # identity map if element has no handler
        $Element = $this->handle($Element);

        $hasName = isset($Element['name']);

        $markup = '';

        if ($hasName)
        {
            $markup .= '<' . $Element['name'];

            if (isset($Element['attributes']))
            {
                foreach ($Element['attributes'] as $name => $value)
                {
                    if ($value === null)
                    {
                        continue;
                    }

                    $markup .= " $name=\"".self::escape($value).'"';
                }
            }
        }

        $permitRawHtml = false;

        if (isset($Element['text']))
        {
            $text = $Element['text'];
        }
        // very strongly consider an alternative if you're writing an
        // extension
        elseif (isset($Element['rawHtml']))
        {
            $text = $Element['rawHtml'];

            $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode'];
            $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode;
        }

        $hasContent = isset($text) || isset($Element['element']) || isset($Element['elements']);

        if ($hasContent)
        {
            $markup .= $hasName ? '>' : '';

            if (isset($Element['elements']))
            {
                $markup .= $this->elements($Element['elements']);
            }
            elseif (isset($Element['element']))
            {
                $markup .= $this->element($Element['element']);
            }
            else
            {
                if (!$permitRawHtml)
                {
                    $markup .= self::escape($text, true);
                }
                else
                {
                    $markup .= $text;
                }
            }

            $markup .= $hasName ? '</' . $Element['name'] . '>' : '';
        }
        elseif ($hasName)
        {
            $markup .= ' />';
        }

        return $markup;
    }

    protected function elements(array $Elements)
    {
        $markup = '';

        $autoBreak = true;

        foreach ($Elements as $Element)
        {
            if (empty($Element))
            {
                continue;
            }

            $autoBreakNext = (isset($Element['autobreak'])
                ? $Element['autobreak'] : isset($Element['name'])
            );
            // (autobreak === false) covers both sides of an element
            $autoBreak = !$autoBreak ? $autoBreak : $autoBreakNext;

            $markup .= ($autoBreak ? "\n" : '') . $this->element($Element);
            $autoBreak = $autoBreakNext;
        }

        $markup .= $autoBreak ? "\n" : '';

        return $markup;
    }

    # ~

    protected function li($lines)
    {
        $Elements = $this->linesElements($lines);

        if ( ! in_array('', $lines)
            and isset($Elements[0]) and isset($Elements[0]['name'])
            and $Elements[0]['name'] === 'p'
        ) {
            unset($Elements[0]['name']);
        }

        return $Elements;
    }

    #
    # AST Convenience
    #

    /**
     * Replace occurrences $regexp with $Elements in $text. Return an array of
     * elements representing the replacement.
     */
    protected static function pregReplaceElements($regexp, $Elements, $text)
    {
        $newElements = array();

        while (preg_match($regexp, $text, $matches, PREG_OFFSET_CAPTURE))
        {
            $offset = $matches[0][1];
            $before = substr($text, 0, $offset);
            $after = substr($text, $offset + strlen($matches[0][0]));

            $newElements[] = array('text' => $before);

            foreach ($Elements as $Element)
            {
                $newElements[] = $Element;
            }

            $text = $after;
        }

        $newElements[] = array('text' => $text);

        return $newElements;
    }

    #
    # Deprecated Methods
    #

    function parse($text)
    {
        $markup = $this->text($text);

        return $markup;
    }

    protected function sanitiseElement(array $Element)
    {
        static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
        static $safeUrlNameToAtt  = array(
            'a'   => 'href',
            'img' => 'src',
        );

        if ( ! isset($Element['name']))
        {
            unset($Element['attributes']);
            return $Element;
        }

        if (isset($safeUrlNameToAtt[$Element['name']]))
        {
            $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
        }

        if ( ! empty($Element['attributes']))
        {
            foreach ($Element['attributes'] as $att => $val)
            {
                # filter out badly parsed attribute
                if ( ! preg_match($goodAttribute, $att))
                {
                    unset($Element['attributes'][$att]);
                }
                # dump onevent attribute
                elseif (self::striAtStart($att, 'on'))
                {
                    unset($Element['attributes'][$att]);
                }
            }
        }

        return $Element;
    }

    protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
    {
        foreach ($this->safeLinksWhitelist as $scheme)
        {
            if (self::striAtStart($Element['attributes'][$attribute], $scheme))
            {
                return $Element;
            }
        }

        $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);

        return $Element;
    }

    #
    # Static Methods
    #

    protected static function escape($text, $allowQuotes = false)
    {
        return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
    }

    protected static function striAtStart($string, $needle)
    {
        $len = strlen($needle);

        if ($len > strlen($string))
        {
            return false;
        }
        else
        {
            return strtolower(substr($string, 0, $len)) === strtolower($needle);
        }
    }

    static function instance($name = 'default')
    {
        if (isset(self::$instances[$name]))
        {
            return self::$instances[$name];
        }

        $instance = new static();

        self::$instances[$name] = $instance;

        return $instance;
    }

    private static $instances = array();

    #
    # Fields
    #

    protected $DefinitionData;

    #
    # Read-Only

    protected $specialCharacters = array(
        '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', '~'
    );

    protected $StrongRegex = array(
        '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*+[*])+?)[*]{2}(?![*])/s',
        '_' => '/^__((?:\\\\_|[^_]|_[^_]*+_)+?)__(?!_)/us',
    );

    protected $EmRegex = array(
        '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
        '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
    );

    protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+';

    protected $voidElements = array(
        'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
    );

    protected $textLevelElements = array(
        'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
        'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
        'i', 'rp', 'del', 'code',          'strike', 'marquee',
        'q', 'rt', 'ins', 'font',          'strong',
        's', 'tt', 'kbd', 'mark',
        'u', 'xm', 'sub', 'nobr',
                   'sup', 'ruby',
                   'var', 'span',
                   'wbr', 'time',
    );
}
\ No newline at end of file