diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..c385c34
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# Drupal editor configuration normalization
+# @see http://editorconfig.org/
+
+# This is the top-most .editorconfig file; do not search in parent directories.
+root = true
+
+# All files.
+[*]
+end_of_line = LF
+indent_style = space
+indent_size = 4
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[{composer.json,composer.lock}]
+indent_size = 4
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..01384d6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+web/bundles/
+web/packages*.json
+web/providers-*.json
+web/p/
+app/config/parameters.yml
+app/bootstrap*
+app/cache/*
+app/logs/*
+bin/
+build/
+vendor/
+/.settings
+/.buildpath
+/.project
+/.idea
+composer.phar
+/nbproject
+.php_cs.cache
+.vagrant
diff --git a/.php-version b/.php-version
new file mode 100644
index 0000000..2df33d7
--- /dev/null
+++ b/.php-version
@@ -0,0 +1 @@
+5.6
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8cfca95
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,36 @@
+language: php
+
+sudo: false
+
+env:
+  global:
+    secure: E9/Y2vJv4GGGdjRZUJuL31q79C2dbgL1yNcclE4MIPXrPWe5f0LuQMwCGbaGN+b6u/VX6YQL6OcFt+VsxL5e439EoYMMY+MmUeDnq5jRzPOVZqgXlNZAxh4KzoNh3Q2nXp6uSr+yaNCFedcx1Os7n5CoLwqji+TATsTPZ1EEDqI=
+
+cache:
+  directories:
+    - $HOME/.composer/cache
+
+services:
+  - redis-server
+
+php:
+  - 5.6
+  - 7.0
+
+matrix:
+  fast_finish: true
+
+before_script:
+    - cp app/config/parameters.yml.dist app/config/parameters.yml
+    - 'echo "    github.client_id: $GH_CLIENT_ID" >> app/config/parameters.yml'
+    - 'echo "    github.client_secret: $GH_CLIENT_SECRET" >> app/config/parameters.yml'
+    - 'curl -sSL https://raw.githubusercontent.com/moliware/travis-solr/master/travis-solr.sh | SOLR_VERSION=3.6.0 SOLR_CONFS="doc" SOLR_CORE="collection1" bash'
+    - composer install
+    - app/console doctrine:database:create --env=test -n
+    - app/console doctrine:schema:create --env=test -n
+
+script:
+    - phpunit
+
+git:
+  depth: 5
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..51efd88
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Jordi Boggiano, Nils Adermann
+
+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.
diff --git a/README.md b/README.md
index 2a091e3..6a105ed 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,89 @@
-# This project is deprecated, use [https://packages.drupal.org/8](https://packages.drupal.org/8) instead. Please read [Using packages.drupal.org](https://www.drupal.org/docs/develop/using-composer/using-packagesdrupalorg) for more information.
+(Drupal) Packagist
+=========
+
+This is a hacked up fork of packagist for use with Drupal. The forking and
+hacking was done for want of a fast way to experiment with the problem domain
+while developing the Drupal-specific functionality separately.
+
+All things considered, it would be best to provide separate repositories for the
+following:
+
+* The Packagist/WebBundle by itself
+* HA functionality -- mostly the queueing used to bootstrap by traversing all
+  the drupal.org project repos with worker nodes
+* Drupal-specific applications for the above
+* Drupal CLI tools for parsing update and release info as a thing unto itself
+
+Instead, we have added the queuing and Drupal-specific functionality in place.
+The main workflow so far has been to install the application as normal and then
+populate the database so that you can generate a composer repository like so:
+
+```
+./app/console packagist:bulk_add --repo-pattern \
+'https://git.drupal.org/project/%2$s' --vendor drupal $(curl \
+https://drupal.org/files/releases.tsv | grep 7.x | awk '{ print $3 }' | sort | uniq -)
+```
+
+Running 10 AWS c3.large instances to consume the queue filled by the
+`packagist:bulk_add` command allows the process to complete in a few hours.
+
+Experimental support for automatic updates has been added in the form of
+a foreground package upsert command that gets invoked by the
+`packagist:drupal_org_update` command, which parses the drupal 7 new releases
+rss feed. You would need to invoke this command with cron or similar to keep the
+application up to date with drupal.org and you would need to monitor disk space
+since the package information is read by cloning a bare repo from drupal.org and
+never removing it. You could consider updating the `drupal/parse_composer`
+project to add an appropriate cleanup method to the Repository class there, or
+just sweep out the cache directory composer uses at the end of the cron job.
+
+Unfortunately, the rss feed references the projects by drupal module name, which
+is always snake_case, but the repo URL is case sensitive and therefore stupid
+project names with uppercase letters will cause things to break. The only
+obvious workaround would be to periodically run through the releases.tsv. In
+limited sampling, only useless modules had this problem.
+
+Package Repository Website for Composer, see the [about page](http://packagist.org/about) on [packagist.org](http://packagist.org/) for more.
+
+Requirements
+------------
+
+- MySQL for the main data store
+- Redis for some functionality (favorites, download statistics)
+- Solr for search
+- git/svn/hg depending on which repositories you want to support
+
+Installation
+------------
+
+1. Clone the repository
+2. Edit `app/config/parameters.yml` and change the relevant values for your setup.
+3. Install dependencies: `php composer.phar install`
+4. Run `app/console doctrine:schema:create` to setup the DB.
+5. Run `app/console assets:install web` to deploy the assets on the web dir.
+6. Make a VirtualHost with DocumentRoot pointing to web/
+
+You should now be able to access the site, create a user, etc.
+
+Setting up search
+-----------------
+
+The search index uses [Solr](http://lucene.apache.org/solr/) 3.6, so you will have to install that on your server.
+If you are running it on a non-standard host or port, you will have to adjust the configuration. See the
+[NelmioSolariumBundle](https://github.com/nelmio/NelmioSolariumBundle) for more details.
+
+You will also have to configure Solr. Use the `schema.xml` provided in the doc/ directory for that.
+
+To index packages, just run `app/console packagist:index`. It is recommended to set up a cron job for
+this command, and have it run every few minutes.
+
+Day-to-Day Operation
+--------------------
+
+There are a few commands you should run periodically (ideally set up a cron job running every minute or so):
+
+    app/console packagist:update --no-debug --env=prod
+    app/console packagist:dump --no-debug --env=prod
+    app/console packagist:index --no-debug --env=prod
+
+The latter is optional and only required if you are running a solr server.
diff --git a/app/.htaccess b/app/.htaccess
new file mode 100644
index 0000000..3418e55
--- /dev/null
+++ b/app/.htaccess
@@ -0,0 +1 @@
+deny from all
\ No newline at end of file
diff --git a/app/AppCache.php b/app/AppCache.php
new file mode 100644
index 0000000..1691ca0
--- /dev/null
+++ b/app/AppCache.php
@@ -0,0 +1,19 @@
+<?php
+
+use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
+
+class AppCache extends HttpCache
+{
+    protected function getOptions()
+    {
+        return array(
+            'debug'                  => false,
+            'default_ttl'            => 0,
+            'private_headers'        => array(),
+            'allow_reload'           => false,
+            'allow_revalidate'       => false,
+            'stale_while_revalidate' => 60,
+            'stale_if_error'         => 86400,
+        );
+    }
+}
\ No newline at end of file
diff --git a/app/AppKernel.php b/app/AppKernel.php
new file mode 100644
index 0000000..2d90a1f
--- /dev/null
+++ b/app/AppKernel.php
@@ -0,0 +1,45 @@
+<?php
+
+use Symfony\Component\Config\Loader\LoaderInterface;
+use Symfony\Component\Debug\ErrorHandler;
+use Symfony\Component\HttpKernel\Kernel;
+
+class AppKernel extends Kernel
+{
+    public function registerBundles()
+    {
+        $bundles = array(
+            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
+            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
+            new Symfony\Bundle\TwigBundle\TwigBundle(),
+            new Symfony\Bundle\MonologBundle\MonologBundle(),
+            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
+            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
+            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
+            new FOS\UserBundle\FOSUserBundle(),
+            new HWI\Bundle\OAuthBundle\HWIOAuthBundle(),
+            new Snc\RedisBundle\SncRedisBundle(),
+            new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
+            new Nelmio\SolariumBundle\NelmioSolariumBundle(),
+            new Nelmio\SecurityBundle\NelmioSecurityBundle(),
+            new Knp\Bundle\MenuBundle\KnpMenuBundle(),
+
+            new Packagist\WebBundle\PackagistWebBundle(),
+          
+            new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(),
+            new DrupalPackagist\Bundle\DrupalPackagistBundle(),
+        );
+
+        if (in_array($this->getEnvironment(), array('dev', 'test'))) {
+            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
+            $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
+        }
+
+        return $bundles;
+    }
+
+    public function registerContainerConfiguration(LoaderInterface $loader)
+    {
+        $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
+    }
+}
diff --git a/app/Resources/FOSUserBundle/views/ChangePassword/changePassword_content.html.twig b/app/Resources/FOSUserBundle/views/ChangePassword/changePassword_content.html.twig
new file mode 100644
index 0000000..71969ff
--- /dev/null
+++ b/app/Resources/FOSUserBundle/views/ChangePassword/changePassword_content.html.twig
@@ -0,0 +1,34 @@
+{{ form_start(form, { 'action': path('fos_user_change_password'), 'attr': { 'class': 'fos_user_change_password col-md-6' } }) }}
+    {{ form_errors(form) }}
+
+    <div class="form-group clearfix">
+        {{ form_label(form.current_password, 'Current password') }}
+        <div class="input-group">
+            {{ form_errors(form.current_password) }}
+            {{ form_widget(form.current_password) }}
+            <span class="input-group-addon"><span class="icon-key"></span></span>
+        </div>
+    </div>
+
+    <div class="form-group clearfix">
+        {{ form_label(form.plainPassword.first, 'New password') }}
+        <div class="input-group">
+            {{ form_errors(form.plainPassword.first) }}
+            {{ form_widget(form.plainPassword.first) }}
+            <span class="input-group-addon"><span class="icon-lock"></span></span>
+        </div>
+    </div>
+
+    <div class="form-group clearfix">
+        {{ form_label(form.plainPassword.second, 'Confirm new password') }}
+        <div class="input-group">
+            {{ form_errors(form.plainPassword.second) }}
+            {{ form_widget(form.plainPassword.second) }}
+            <span class="input-group-addon"><span class="icon-lock-open"></span></span>
+        </div>
+    </div>
+
+    {{ form_widget(form) }}
+
+    <input type="submit" class="btn btn-block btn-inverse btn-lg" value="{{ 'change_password.submit'|trans({}, 'FOSUserBundle') }}" />
+{{ form_end(form) }}
diff --git a/app/Resources/FOSUserBundle/views/Profile/edit_content.html.twig b/app/Resources/FOSUserBundle/views/Profile/edit_content.html.twig
new file mode 100644
index 0000000..5540be3
--- /dev/null
+++ b/app/Resources/FOSUserBundle/views/Profile/edit_content.html.twig
@@ -0,0 +1,54 @@
+<form action="{{ path('fos_user_profile_edit') }}" {{ form_enctype(form) }} method="POST" class="fos_user_profile_edit col-md-6">
+    {{ form_errors(form) }}
+
+    <div class="form-group clearfix">
+        {{ form_label(form.username) }}
+        <div class="input-group">
+            {{ form_errors(form.username) }}
+            {{ form_widget(form.username) }}
+            <span class="input-group-addon"><span class="icon-user"></span></span>
+        </div>
+    </div>
+
+    <div class="form-group clearfix">
+        {{ form_label(form.email) }}
+        <div class="input-group clearfix">
+            {{ form_errors(form.email) }}
+            {{ form_widget(form.email) }}
+            <span class="input-group-addon"><span class="icon-mail"></span></span>
+        </div>
+    </div>
+
+    <div class="form-group clearfix">
+        {{ form_label(form.current_password) }}
+        <div class="input-group">
+            {{ form_errors(form.current_password) }}
+            {{ form_widget(form.current_password) }}
+            <span class="input-group-addon"><span class="icon-lock"></span></span>
+        </div>
+    </div>
+
+    <div class="notifications form-group">
+        {{ form_errors(form.failureNotifications) }}
+        <label>
+            {{ form_widget(form.failureNotifications) }}
+            Notify me of package update failures
+        </label>
+    </div>
+
+    {{ form_rest(form) }}
+
+    <input type="submit" class="btn btn-block btn-success btn-lg" value="{{ 'profile.edit.submit'|trans({}, 'FOSUserBundle') }}" />
+
+    <hr>
+
+    <h5>Using GitHub:</h5>
+
+    <a href="{{ app.user.githubId ? '#' : hwi_oauth_login_url('github') }}" class="btn btn-block btn-github btn-lg {{ app.user.githubId ? 'disabled' : 'btn-primary' }}">
+        <span class="icon-github"></span>
+        {{ app.user.githubId ? 'Accounts connected' : 'Connect accounts' }}
+    </a>
+</form>
+
+<div class="col-md-2">
+</div>
diff --git a/app/Resources/FOSUserBundle/views/Profile/show.html.twig b/app/Resources/FOSUserBundle/views/Profile/show.html.twig
new file mode 100644
index 0000000..d054985
--- /dev/null
+++ b/app/Resources/FOSUserBundle/views/Profile/show.html.twig
@@ -0,0 +1,28 @@
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% import "PackagistWebBundle::macros.html.twig" as macros %}
+
+{% block fos_user_content %}
+<section class="col-md-9">
+    {%- if app.user.apiToken %}
+        <h3 class="font-normal profile-title">Your API Token</h3>
+
+        <div class="input-group api-token-group clearfix">
+            <input id="api-token" type="text" class="form-control" value="" data-api-token="{{ app.user.apiToken }}" readonly="readonly">
+            <span class="input-group">
+                <button class="btn btn-success btn-show-api-token" type="button">Show API Token</button>
+            </span>
+        </div>
+
+        <p>You can use your API token to interact with the Packagist API, see details in <a href="{{ path('about') }}#how-to-update-packages">the docs</a>.</p>
+
+        <hr>
+    {%- endif %}
+
+    {% embed "PackagistWebBundle:Web:list.html.twig" with {noLayout: 'true', showAutoUpdateWarning: true} %}
+        {% block content_title %}
+            <h3 class="font-normal">Your packages</h3>
+        {% endblock %}
+    {% endembed %}
+</section>
+{% endblock %}
diff --git a/app/Resources/FOSUserBundle/views/Registration/register_content.html.twig b/app/Resources/FOSUserBundle/views/Registration/register_content.html.twig
new file mode 100644
index 0000000..160aee5
--- /dev/null
+++ b/app/Resources/FOSUserBundle/views/Registration/register_content.html.twig
@@ -0,0 +1,53 @@
+{% trans_default_domain 'FOSUserBundle' %}
+
+<div class="col-md-12">
+    <h2 class="title">
+        Create account
+    </h2>
+</div>
+
+<form action="{{ path('fos_user_registration_register') }}" method="POST" class="fos_user_registration_register col-md-6">
+    {% if form is defined %}
+        {{ form_errors(form) }}
+    {% endif %}
+
+    <div class="form-group clearfix">
+        <label for="fos_user_registration_email">Email:</label>
+        <div class="input-group clearfix">
+            {{ form_errors(form.email) }}
+            {{ form_widget(form.email) }}
+            <span class="input-group-addon"><span class="icon-mail"></span></span>
+        </div>
+    </div>
+
+    <div class="form-group clearfix">
+        <label for="fos_user_registration_username">Username:</label>
+        <div class="input-group clearfix">
+            {{ form_errors(form.username) }}
+            {{ form_widget(form.username) }}
+            <span class="input-group-addon"><span class="icon-user"></span></span>
+        </div>
+    </div>
+
+    <div class="form-group clearfix">
+        <label for="fos_user_registration_plainPassword_first">Password:</label>
+        <div class="input-group clearfix">
+            {{ form_errors(form.plainPassword.first) }}
+            {{ form_widget(form.plainPassword.first) }}
+            <span class="input-group-addon"><span class="icon-lock"></span></span>
+        </div>
+    </div>
+
+    <div class="form-group clearfix">
+        <label for="fos_user_registration_plainPassword_second">Repeat Password:</label>
+        <div class="input-group clearfix">
+            {{ form_errors(form.plainPassword.second) }}
+            {{ form_widget(form.plainPassword.second) }}
+            <span class="input-group-addon"><span class="icon-lock"></span></span>
+        </div>
+    </div>
+
+    {{ form_widget(form._token) }}
+
+    <input type="submit" class="btn btn-block btn-success btn-lg" value="{{ 'registration.submit'|trans }}" />
+</form>
diff --git a/app/Resources/FOSUserBundle/views/Resetting/request.html.twig b/app/Resources/FOSUserBundle/views/Resetting/request.html.twig
new file mode 100644
index 0000000..973cb98
--- /dev/null
+++ b/app/Resources/FOSUserBundle/views/Resetting/request.html.twig
@@ -0,0 +1,29 @@
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% trans_default_domain 'FOSUserBundle' %}
+
+{% block fos_user_header %}
+<h2 class="title">
+    Resetting password
+</h2>
+{% endblock %}
+
+{% block fos_user_content %}
+<form action="{{ path('fos_user_resetting_send_email') }}" method="POST" class="fos_user_resetting_request col-md-6">
+    {% if invalid_username is defined %}
+        <div class="alert alert-warning">
+            <p>{{ 'resetting.request.invalid_username'|trans({'%username%': invalid_username}) }}</p>
+        </div>
+    {% endif %}
+
+    <div class="form-group">
+        <label for="username">{{ 'resetting.request.username'|trans }}</label>
+        <div class="input-group">
+            <input type="text" id="username" name="username" required="required" class="form-control" />
+            <span class="input-group-addon"><span class="icon-user"></span></span>
+        </div>
+    </div>
+
+    <input type="submit" class="btn btn-block btn-inverse btn-lg" value="{{ 'resetting.request.submit'|trans }}" />
+</form>
+{% endblock %}
diff --git a/app/Resources/FOSUserBundle/views/layout.html.twig b/app/Resources/FOSUserBundle/views/layout.html.twig
new file mode 100644
index 0000000..38dfbbe
--- /dev/null
+++ b/app/Resources/FOSUserBundle/views/layout.html.twig
@@ -0,0 +1,23 @@
+{% extends 'PackagistWebBundle::layout.html.twig' %}
+
+{% block content %}
+{% if app.user is null %}
+    {{ block('fos_user_header') }}
+
+    <section class="row">
+        {{ block('fos_user_content') }}
+    </section>
+{% else %}
+    <h2 class="title">
+        {{ app.user.username }}
+    </h2>
+
+    <section class="row">
+        <div class="col-md-3">
+            {{ knp_menu_render('profile_menu', {'currentClass': 'active', 'allow_safe_labels': true}) }}
+        </div>
+
+        {{ block('fos_user_content') }}
+    </section>
+{% endif %}
+{% endblock %}
diff --git a/app/Resources/HWIOAuthBundle/views/Connect/connect_confirm.html.twig b/app/Resources/HWIOAuthBundle/views/Connect/connect_confirm.html.twig
new file mode 100644
index 0000000..f3c0b91
--- /dev/null
+++ b/app/Resources/HWIOAuthBundle/views/Connect/connect_confirm.html.twig
@@ -0,0 +1,23 @@
+{% extends 'HWIOAuthBundle::layout.html.twig' %}
+
+{% block hwi_oauth_content %}
+    <div class="col-xs-12">
+        <h2 class="title">{{ 'header.connecting' | trans({}, 'HWIOAuthBundle')}}</h2>
+    </div>
+
+    <div class="col-md-6">
+        <p>{{ 'connect.confirm.text' | trans({'%service%': service | trans({}, 'HWIOAuthBundle'), '%name%': userInformation.realName}, 'HWIOAuthBundle') }}</p>
+        <form action="{{ path('hwi_oauth_connect_service', {'service': service, 'key': key}) }}" {{ form_enctype(form) }} method="POST" class="fos_user_registration_register">
+            {{ form_widget(form) }}
+            <div>
+                <button type="submit" class="btn btn-primary">{{ 'connect.confirm.submit' | trans({}, 'HWIOAuthBundle') }}</button>
+                <a href="{{ path('hwi_oauth_connect') }}" class="btn btn-warning">{{ 'connect.confirm.cancel' | trans({}, 'HWIOAuthBundle') }}</a>
+            </div>
+        </form>
+    </div>
+    <div class="col-md-6">
+        {% if userInformation.profilePicture is defined and userInformation.profilePicture is not empty %}
+            <img src="{{ userInformation.profilePicture }}" />
+        {% endif %}
+    </div>
+{% endblock hwi_oauth_content %}
diff --git a/app/Resources/HWIOAuthBundle/views/Connect/connect_success.html.twig b/app/Resources/HWIOAuthBundle/views/Connect/connect_success.html.twig
new file mode 100644
index 0000000..649eb53
--- /dev/null
+++ b/app/Resources/HWIOAuthBundle/views/Connect/connect_success.html.twig
@@ -0,0 +1,9 @@
+{% extends 'HWIOAuthBundle::layout.html.twig' %}
+
+{% block hwi_oauth_content %}
+    <div class="col-xs-12">
+        <h2 class="title">GitHub Login</h2>
+        <p>{{ 'header.success' | trans({'%name%': userInformation.realName}, 'HWIOAuthBundle') }}</p>
+        <p>Continue to <a href="{{ path('fos_user_profile_show') }}">your profile</a>.</p>
+    </div>
+{% endblock hwi_oauth_content %}
diff --git a/app/Resources/HWIOAuthBundle/views/Connect/login.html.twig b/app/Resources/HWIOAuthBundle/views/Connect/login.html.twig
new file mode 100644
index 0000000..cd265df
--- /dev/null
+++ b/app/Resources/HWIOAuthBundle/views/Connect/login.html.twig
@@ -0,0 +1,70 @@
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% trans_default_domain 'FOSUserBundle' %}
+
+{% block fos_user_header %}
+{% endblock %}
+
+{% block fos_user_content %}
+    {# HWIOAuthBundle uses the same template for the login and the connect functionality currently
+       so we need to check if the user is already authenticated. #}
+
+        {% if not app.user %}
+            <div class="col-xs-12">
+                <h2 class="title">
+                    Sign in
+                </h2>
+            </div>
+            <div class="col-md-6">
+                {% if error is defined and error is not empty %}
+                    <div class="alert alert-warning">
+                        {{ error }}
+                    </div>
+                {% endif %}
+
+                <form action="{{ path('login_check') }}" method="POST">
+                    <div class="form-group clearfix">
+                        <label for="username">{{ 'security.login.username'|trans({}, 'FOSUserBundle') }}</label>
+                        <div class="input-group clearfix">
+                            <input class="form-control" type="text" id="username" name="_username">
+                            <span class="input-group-addon"><span class="icon-user"></span></span>
+                        </div>
+                    </div>
+
+                    <div class="form-group clearfix">
+                        <label for="password">{{ 'security.login.password'|trans({}, 'FOSUserBundle') }}</label>
+                        <div class="input-group clearfix">
+                            <input class="form-control" type="password" id="password" name="_password">
+                            <span class="input-group-addon"><span class="icon-lock"></span></span>
+                        </div>
+                    </div>
+
+                    <div class="checkbox">
+                        <a class="pull-right" href="{{ path('fos_user_resetting_request') }}">Forgot password?</a>
+
+                        <label for="remember_me">
+                            <input type="checkbox" id="remember_me" name="_remember_me" value="on" checked="checked" />
+                            {{- 'security.login.remember_me'|trans({}, 'FOSUserBundle') }}
+                        </label>
+                    </div>
+
+                    {% if packagist_host and packagist_host in app.request.headers.get('Referer') %}
+                        <input type="hidden" name="_target_path" value="{{ app.request.headers.get('Referer') }}" />
+                    {% endif %}
+
+                    <input class="btn btn-block btn-success btn-lg" type="submit" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans({}, 'FOSUserBundle') }}" />
+                </form>
+
+                <hr>
+            </div>
+        {% endif %}
+
+        {% for owner in hwi_oauth_resource_owners() %}
+            {% if not app.user %}
+                <div class="clearfix"></div>
+            {% endif %}
+            <div class="col-md-6">
+                <a class="btn btn-block btn-{{ owner }} btn-primary btn-lg" href="{{ hwi_oauth_login_url(owner) }}"><span class="icon-{{ owner }}"></span>Login with {{ owner | trans({}, 'HWIOAuthBundle') }}</a>{% if not loop.last %}<br />{% endif %}
+            </div>
+        {% endfor %}
+{% endblock %}
diff --git a/app/Resources/HWIOAuthBundle/views/Connect/registration.html.twig b/app/Resources/HWIOAuthBundle/views/Connect/registration.html.twig
new file mode 100644
index 0000000..2e95adb
--- /dev/null
+++ b/app/Resources/HWIOAuthBundle/views/Connect/registration.html.twig
@@ -0,0 +1,21 @@
+{% extends 'HWIOAuthBundle::layout.html.twig' %}
+
+{% block hwi_oauth_content %}
+    <div class="col-xs-12">
+        <h2 class="title">{{ 'header.register' | trans({'%name%': userInformation.realName}, 'HWIOAuthBundle') }}</h2>
+    </div>
+    <div class="col-md-6">
+        <form action="{{ path('hwi_oauth_connect_registration', {'key': key}) }}" {{ form_enctype(form) }} method="POST" class="hwi_oauth_registration_register">
+            {{ form_widget(form) }}
+            <div>
+                <button type="submit" class="btn btn-primary">{{ 'connect.registration.submit'|trans({}, 'HWIOAuthBundle') }}</button>
+                <a href="{{ path('hwi_oauth_connect') }}" class="btn btn-warning">{{ 'connect.registration.cancel' | trans({}, 'HWIOAuthBundle') }}</a>
+            </div>
+        </form>
+    </div>
+    <div class="col-md-6">
+        {% if userInformation.profilePicture is not empty %}
+            <img src="{{ userInformation.profilePicture }}" />
+        {% endif %}
+    </div>
+{% endblock hwi_oauth_content %}
diff --git a/app/Resources/HWIOAuthBundle/views/Connect/registration_success.html.twig b/app/Resources/HWIOAuthBundle/views/Connect/registration_success.html.twig
new file mode 100644
index 0000000..fd03e59
--- /dev/null
+++ b/app/Resources/HWIOAuthBundle/views/Connect/registration_success.html.twig
@@ -0,0 +1,9 @@
+{% extends 'HWIOAuthBundle::layout.html.twig' %}
+
+{% block hwi_oauth_content %}
+    <div class="col-xs-12">
+        <h2 class="title">GitHub Login</h2>
+        <p>{{ 'header.registration_success' | trans({'%username%': app.user.username}, 'HWIOAuthBundle') }}</p>
+        <p>Continue to <a href="{{ path('fos_user_profile_show') }}">your profile</a>.</p>
+    </div>
+{% endblock hwi_oauth_content %}
diff --git a/app/Resources/HWIOAuthBundle/views/layout.html.twig b/app/Resources/HWIOAuthBundle/views/layout.html.twig
new file mode 100644
index 0000000..236d8bd
--- /dev/null
+++ b/app/Resources/HWIOAuthBundle/views/layout.html.twig
@@ -0,0 +1,7 @@
+{% extends 'PackagistWebBundle::layout.html.twig' %}
+
+{% block content %}
+<section class="row">
+     {% block hwi_oauth_content %}{% endblock %}
+</section>
+{% endblock %}
diff --git a/app/Resources/TwigBundle/views/Exception/error404.html.twig b/app/Resources/TwigBundle/views/Exception/error404.html.twig
new file mode 100644
index 0000000..a707595
--- /dev/null
+++ b/app/Resources/TwigBundle/views/Exception/error404.html.twig
@@ -0,0 +1,13 @@
+{% extends 'PackagistWebBundle::layout.html.twig' %}
+
+{% block content %}
+<section class="wrapper">
+    <section class="container error">
+        <div class="row">
+            <h2 class="title">Oh noes, 404!</h2>
+            <h3 class="font-normal">It looks like you requested a page that was not found.</h3>
+            <h4>Go back to the <a href="{{ path('home') }}">homepage</a> or use the search above to find the package you want.</h4>
+        </div>
+    </section>
+</section>
+{% endblock %}
diff --git a/app/Resources/translations/FOSUserBundle.en.yml b/app/Resources/translations/FOSUserBundle.en.yml
new file mode 100644
index 0000000..152e2bf
--- /dev/null
+++ b/app/Resources/translations/FOSUserBundle.en.yml
@@ -0,0 +1,10 @@
+# Password resetting
+resetting:
+    email: |
+        Reset Password
+        Hello %username%
+
+        To reset your password - please visit %confirmationUrl%
+
+        Regards,
+        The Packagist Team
diff --git a/app/Resources/views/base_nolayout.html.twig b/app/Resources/views/base_nolayout.html.twig
new file mode 100644
index 0000000..372c1bb
--- /dev/null
+++ b/app/Resources/views/base_nolayout.html.twig
@@ -0,0 +1 @@
+{% block content %}{% endblock %}
\ No newline at end of file
diff --git a/app/autoload.php b/app/autoload.php
new file mode 100644
index 0000000..26a201c
--- /dev/null
+++ b/app/autoload.php
@@ -0,0 +1,21 @@
+<?php
+
+use Doctrine\Common\Annotations\AnnotationRegistry;
+
+if (!$loader = @include __DIR__.'/../vendor/autoload.php') {
+
+    $message = <<< EOF
+<p>You must set up the project dependencies by running composer install</p>
+
+EOF;
+
+    if (PHP_SAPI === 'cli') {
+        $message = strip_tags($message);
+    }
+
+    die($message);
+}
+
+AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
+
+return $loader;
diff --git a/app/check.php b/app/check.php
new file mode 100644
index 0000000..c10f1ab
--- /dev/null
+++ b/app/check.php
@@ -0,0 +1,84 @@
+<?php
+
+if (!$iniPath = get_cfg_var('cfg_file_path')) {
+    $iniPath = 'WARNING: not using a php.ini file';
+}
+
+echo "********************************\n";
+echo "*                              *\n";
+echo "*  Symfony requirements check  *\n";
+echo "*                              *\n";
+echo "********************************\n\n";
+echo sprintf("php.ini used by PHP: %s\n\n", $iniPath);
+
+echo "** WARNING **\n";
+echo "*  The PHP CLI can use a different php.ini file\n";
+echo "*  than the one used with your web server.\n";
+if ('\\' == DIRECTORY_SEPARATOR) {
+    echo "*  (especially on the Windows platform)\n";
+}
+echo "*  If this is the case, please ALSO launch this\n";
+echo "*  utility from your web server.\n";
+echo "** WARNING **\n";
+
+// mandatory
+echo_title("Mandatory requirements");
+check(version_compare(phpversion(), '5.3.2', '>='), sprintf('Checking that PHP version is at least 5.3.2 (%s installed)', phpversion()), 'Install PHP 5.3.2 or newer (current version is '.phpversion(), true);
+check(ini_get('date.timezone'), 'Checking that the "date.timezone" setting is set', 'Set the "date.timezone" setting in php.ini (like Europe/Paris)', true);
+check(is_writable(__DIR__.'/../app/cache'), sprintf('Checking that app/cache/ directory is writable'), 'Change the permissions of the app/cache/ directory so that the web server can write in it', true);
+check(is_writable(__DIR__.'/../app/logs'), sprintf('Checking that the app/logs/ directory is writable'), 'Change the permissions of the app/logs/ directory so that the web server can write in it', true);
+check(function_exists('json_encode'), 'Checking that the json_encode() is available', 'Install and enable the json extension', true);
+
+// warnings
+echo_title("Optional checks");
+check(class_exists('DomDocument'), 'Checking that the PHP-XML module is installed', 'Install and enable the php-xml module', false);
+check(defined('LIBXML_COMPACT'), 'Checking that the libxml version is at least 2.6.21', 'Upgrade your php-xml module with a newer libxml', false);
+check(function_exists('token_get_all'), 'Checking that the token_get_all() function is available', 'Install and enable the Tokenizer extension (highly recommended)', false);
+check(function_exists('mb_strlen'), 'Checking that the mb_strlen() function is available', 'Install and enable the mbstring extension', false);
+check(function_exists('iconv'), 'Checking that the iconv() function is available', 'Install and enable the iconv extension', false);
+check(function_exists('utf8_decode'), 'Checking that the utf8_decode() is available', 'Install and enable the XML extension', false);
+check(function_exists('posix_isatty'), 'Checking that the posix_isatty() is available', 'Install and enable the php_posix extension (used to colorized the CLI output)', false);
+check(class_exists('Locale'), 'Checking that the intl extension is available', 'Install and enable the intl extension (used for validators)', false);
+
+$accelerator = 
+    (function_exists('apc_store') && ini_get('apc.enabled'))
+    ||
+    function_exists('eaccelerator_put') && ini_get('eaccelerator.enable')
+    ||
+    function_exists('xcache_set')
+;
+check($accelerator, 'Checking that a PHP accelerator is installed', 'Install a PHP accelerator like APC (highly recommended)', false);
+
+check(!ini_get('short_open_tag'), 'Checking that php.ini has short_open_tag set to off', 'Set short_open_tag to off in php.ini', false);
+check(!ini_get('magic_quotes_gpc'), 'Checking that php.ini has magic_quotes_gpc set to off', 'Set magic_quotes_gpc to off in php.ini', false);
+check(!ini_get('register_globals'), 'Checking that php.ini has register_globals set to off', 'Set register_globals to off in php.ini', false);
+check(!ini_get('session.auto_start'), 'Checking that php.ini has session.auto_start set to off', 'Set session.auto_start to off in php.ini', false);
+
+echo_title("Optional checks (Doctrine)");
+
+check(class_exists('PDO'), 'Checking that PDO is installed', 'Install PDO (mandatory for Doctrine)', false);
+if (class_exists('PDO')) {
+    $drivers = PDO::getAvailableDrivers();
+    check(count($drivers), 'Checking that PDO has some drivers installed: '.implode(', ', $drivers), 'Install PDO drivers (mandatory for Doctrine)');
+}
+
+/**
+ * Checks a configuration.
+ */
+function check($boolean, $message, $help = '', $fatal = false)
+{
+    echo $boolean ? "  OK        " : sprintf("\n\n[[%s]] ", $fatal ? ' ERROR ' : 'WARNING');
+    echo sprintf("$message%s\n", $boolean ? '' : ': FAILED');
+
+    if (!$boolean) {
+        echo "            *** $help ***\n";
+        if ($fatal) {
+            die("You must fix this problem before resuming the check.\n");
+        }
+    }
+}
+
+function echo_title($title)
+{
+    echo "\n** $title **\n\n";
+}
diff --git a/app/config/config.yml b/app/config/config.yml
new file mode 100644
index 0000000..f8ac55a
--- /dev/null
+++ b/app/config/config.yml
@@ -0,0 +1,112 @@
+imports:
+    - { resource: defaults.yml }
+    - { resource: parameters.yml }
+    - { resource: security.yml }
+
+framework:
+    secret:          %secret%
+    router:
+        resource: '%kernel.root_dir%/config/routing.yml'
+        strict_requirements: %kernel.debug%
+    form:            true
+    csrf_protection: true
+    validation:      { enable_annotations: true }
+    translator:      { fallback: en }
+    templating:      { engines: ['twig'] }
+    assets:
+        version: v=%assets_version%
+    default_locale: %locale%
+    session:
+        name:            packagist
+        cookie_lifetime: 3600
+        cookie_httponly: true
+        save_path: %session_save_path%
+    trusted_proxies: %trusted_proxies%
+    trusted_hosts: %trusted_hosts%
+    http_method_override: true
+    fragments: ~
+
+# Twig Configuration
+twig:
+    form_themes:
+        - 'PackagistWebBundle::forms.html.twig'
+    debug:            %kernel.debug%
+    strict_variables: %kernel.debug%
+    globals:
+        google_analytics: %google_analytics%
+        packagist_host: %packagist_host%
+
+# Doctrine Configuration
+doctrine:
+    dbal:
+        driver:   %database_driver%
+        host:     %database_host%
+        dbname:   %database_name%
+        user:     %database_user%
+        password: %database_password%
+        charset:  utf8mb4
+        # See https://github.com/sonata-project/SonataAdminBundle/issues/3342
+        server_version: 5.6
+    orm:
+        auto_generate_proxy_classes: %kernel.debug%
+        auto_mapping: true
+
+snc_redis:
+    clients:
+        default:
+            type: predis
+            alias: default
+            dsn: %redis_dsn%
+            options:
+                profile: 2.8
+    session:
+        client: default
+        prefix: 'sess:'
+        ttl: 3600
+
+# Swiftmailer Configuration
+swiftmailer:
+    transport: %mailer_transport%
+    host:      %mailer_host%
+    username:  %mailer_user%
+    password:  %mailer_password%
+    spool:     { type: memory }
+    disable_delivery: true
+
+fos_user:
+    db_driver:     orm
+    firewall_name: main
+    user_class:  Packagist\WebBundle\Entity\User
+    use_username_form_type: true
+    from_email:
+        address:        %mailer_from_email%
+        sender_name:    %mailer_from_name%
+    profile:
+        form:
+            type:       packagist_user_profile
+
+hwi_oauth:
+    firewall_names: [main]
+    connect:
+        account_connector: packagist.user_provider
+        registration_form_handler: packagist.oauth.registration_form_handler
+        registration_form: packagist.oauth.registration_form
+    fosub:
+        username_iterations: 30
+        properties:
+            github: githubId
+    resource_owners:
+        github:
+            type:          github
+            client_id:     %github.client_id%
+            client_secret: %github.client_secret%
+            options:
+                csrf: true
+
+nelmio_solarium:
+    clients:
+        default:
+            host: %solr_host%
+            port: %solr_port%
+            path: %solr_path%
+            core: %solr_core%
diff --git a/app/config/config_dev.yml b/app/config/config_dev.yml
new file mode 100644
index 0000000..28fac27
--- /dev/null
+++ b/app/config/config_dev.yml
@@ -0,0 +1,28 @@
+imports:
+    - { resource: config.yml }
+
+framework:
+    router:   { resource: '%kernel.root_dir%/config/routing_dev.yml' }
+    profiler: { only_exceptions: false }
+
+web_profiler:
+    toolbar: true
+    intercept_redirects: false
+
+monolog:
+    handlers:
+        main:
+            type:  stream
+            path:  %kernel.logs_dir%/%kernel.environment%.log
+            level: debug
+            channels: ['!event']
+        firephp:
+            type:  firephp
+            level: info
+
+hwi_oauth:
+    http_client:
+        verify_peer: false
+
+#assetic:
+#    use_controller: true
diff --git a/app/config/config_prod.yml b/app/config/config_prod.yml
new file mode 100644
index 0000000..68975ee
--- /dev/null
+++ b/app/config/config_prod.yml
@@ -0,0 +1,52 @@
+imports:
+    - { resource: config.yml }
+
+doctrine:
+    orm:
+        metadata_cache_driver: %doctrine_cache_backend%
+        result_cache_driver: %doctrine_cache_backend%
+        query_cache_driver: %doctrine_cache_backend%
+
+monolog:
+    handlers:
+        main:
+            type:         fingers_crossed
+            action_level: error
+            handler:      nested
+            excluded_404s:
+                - ^/
+        nested:
+            type:  stream
+            path:  %kernel.logs_dir%/%kernel.environment%.log
+            level: debug
+
+snc_redis:
+    clients:
+        default:
+            type: predis
+            alias: default
+            dsn: %redis_dsn%
+            options:
+                profile: 2.8
+        session:
+            type: predis
+            alias: session
+            dsn: %redis_session_dsn%
+    session:
+        client: session
+
+framework:
+    session:
+    session:
+        handler_id: snc_redis.session.handler
+        cookie_secure: %force_ssl%
+    validation:
+        cache: %validation_cache_backend%
+
+nelmio_security:
+    clickjacking:
+        paths:
+            '^/.*': DENY
+    forced_ssl:
+        enabled: %force_ssl%
+        hsts_max_age: 31104000 # 1y
diff --git a/app/config/config_test.yml b/app/config/config_test.yml
new file mode 100644
index 0000000..7553fde
--- /dev/null
+++ b/app/config/config_test.yml
@@ -0,0 +1,18 @@
+imports:
+    - { resource: config_dev.yml }
+
+parameters:
+    database_name: %database_name_test%
+    redis_dsn: %redis_dsn_test%
+
+framework:
+    test: ~
+    session:
+        storage_id: session.storage.filesystem
+
+web_profiler:
+    toolbar: false
+    intercept_redirects: false
+
+swiftmailer:
+    disable_delivery: true
diff --git a/app/config/defaults.yml b/app/config/defaults.yml
new file mode 100644
index 0000000..3a35976
--- /dev/null
+++ b/app/config/defaults.yml
@@ -0,0 +1,12 @@
+parameters:
+    packagist_host: ~
+    packagist_metadata_dir: '%kernel.cache_dir%/composer-packages-build'
+    packagist_dumper_compress: 0
+    session_save_path: %kernel.cache_dir%/sessions
+    database_name_test: packagist_test
+    redis_dsn_test: redis://127.0.0.1/14
+    assets_version: 1234
+    solr_host: 127.0.0.1
+    solr_port: 8983
+    solr_path: /solr
+    solr_core: collection1
diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist
new file mode 100644
index 0000000..72371f2
--- /dev/null
+++ b/app/config/parameters.yml.dist
@@ -0,0 +1,89 @@
+parameters:
+    database_driver: pdo_mysql
+    database_host: localhost
+    database_name: packagist
+    database_user: root
+    database_password:
+    database_name_test: packagist_test
+
+    mailer_transport:
+    mailer_host: localhost
+    mailer_user:
+    mailer_password:
+    mailer_from_email: admin@example.org
+    mailer_from_name: Admin Team
+
+    # packagist_host: example.org
+    # router.request_context.host: %packagist_host%
+    # router.request_context.scheme: https
+
+    redis_dsn: redis://localhost/1
+    redis_dsn_test: redis://127.0.0.1/14
+    redis_session_dsn: redis://localhost/2
+
+    solr_host: 127.0.0.1
+    solr_port: 8983
+    solr_path: /solr
+    solr_core: collection1
+
+    locale: en
+
+    google_analytics:
+        ga_key:
+
+    # set those to values obtained by creating an application at https://github.com/settings/applications
+    github.client_id: CHANGE_ME_IN_PROD
+    github.client_secret: CHANGE_ME_IN_PROD
+
+        # -- performance features --
+        # set both to apc to optimize things if it is available
+    validation_cache_backend: ~
+    doctrine_cache_backend: array
+
+        # -- security features --
+    secret: CHANGE_ME_IN_PROD
+    remember_me.secret: CHANGE_ME_IN_PROD
+
+        # set to true to enforce ssl, make sure you have a proper certificate in place
+    force_ssl: false
+        # e.g. [127.0.0.1] if the app is running behind a reverse proxy on localhost
+    trusted_proxies: ~
+        # e.g. ['.*\.?packagist\.org$'] to allow packagist.org and all subdomains as valid hosts
+    trusted_hosts: ~
+
+old_sound_rabbit_mq:
+    connections:
+        default:
+            host:     'localhost'
+            port:     5672
+            user:     'guest'
+            password: 'guest'
+            vhost:    '/'
+    producers:
+        add_packages:
+            connection: default
+            exchange_options:
+                type: direct
+                name: add-packages
+        update_packages:
+            connection: default
+            exchange_options:
+                type: direct
+                name: update-packages
+    consumers:
+        add_packages:
+            connection: default
+            queue_options:
+                name: add-packages
+            exchange_options:
+                type: direct
+                name: add-packages
+            callback: packagist.background_package_upsert_consumer
+        update_packages:
+            connection: default
+            queue_options:
+                name: update-packages
+            exchange_options:
+                type: direct
+                name: update-packages
+            callback: packagist.background_package_updater_consumer
diff --git a/app/config/routing.yml b/app/config/routing.yml
new file mode 100644
index 0000000..f4128b3
--- /dev/null
+++ b/app/config/routing.yml
@@ -0,0 +1,46 @@
+_packagist:
+    resource: '@PackagistWebBundle/Controller'
+    type:     annotation
+
+fos_user_profile:
+    resource: '@FOSUserBundle/Resources/config/routing/profile.xml'
+    prefix: /profile
+
+fos_user_profile_show:
+    path: /profile/
+    defaults: { _controller: PackagistWebBundle:User:myProfile }
+    methods: [GET]
+
+fos_user_register:
+    resource: '@FOSUserBundle/Resources/config/routing/registration.xml'
+    prefix: /register
+
+fos_user_resetting:
+    resource: '@FOSUserBundle/Resources/config/routing/resetting.xml'
+    prefix: /resetting
+
+fos_user_change_password:
+    resource: '@FOSUserBundle/Resources/config/routing/change_password.xml'
+
+
+hwi_oauth_connect:
+    resource: '@HWIOAuthBundle/Resources/config/routing/connect.xml'
+    prefix:   /connect
+
+# overrides the fosub /login page
+hwi_oauth_login:
+    resource: '@HWIOAuthBundle/Resources/config/routing/login.xml'
+    prefix:   /login
+
+hwi_oauth_redirect:
+    resource: '@HWIOAuthBundle/Resources/config/routing/redirect.xml'
+    prefix:   /login
+
+github_check:
+    path: /login/check-github
+
+logout:
+    path: /logout
+
+login_check:
+    path: /login_check
diff --git a/app/config/routing_dev.yml b/app/config/routing_dev.yml
new file mode 100644
index 0000000..21e3094
--- /dev/null
+++ b/app/config/routing_dev.yml
@@ -0,0 +1,14 @@
+#_assetic:
+#    resource: .
+#    type:     assetic
+
+_wdt:
+    resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
+    prefix:   /_wdt
+
+_profiler:
+    resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
+    prefix:   /_profiler
+
+_main:
+    resource: routing.yml
diff --git a/app/config/security.yml b/app/config/security.yml
new file mode 100644
index 0000000..343ef3c
--- /dev/null
+++ b/app/config/security.yml
@@ -0,0 +1,62 @@
+security:
+    encoders:
+        FOS\UserBundle\Model\UserInterface:
+            algorithm:        sha512
+            encode_as_base64: false
+            iterations:       1
+
+    providers:
+        packagist:
+            id: packagist.user_provider
+
+    firewalls:
+        main:
+            pattern:      .*
+            form_login:
+                provider:       packagist
+                login_path:     /login
+                use_forward:    false
+                check_path:     /login_check
+                failure_path:   null
+            remember_me:
+                secret: %remember_me.secret%
+                user_providers: packagist
+                name: pauth
+                always_remember_me: true
+                lifetime: 31104000 # 1y
+            logout:       true
+            anonymous:    true
+            oauth:
+                resource_owners:
+                    github: '/login/check-github'
+                login_path:        /login
+                failure_path:      /login
+                oauth_user_provider:
+                    service: packagist.user_provider
+            switch_user:
+                provider: packagist
+
+    access_control:
+        # The WDT has to be allowed to anonymous users to avoid requiring the login with the AJAX request
+        - { path: ^/_wdt/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+        - { path: ^/_profiler/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+        # AsseticBundle paths used when using the controller for assets
+        - { path: ^/js/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+        - { path: ^/css/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+        # URL of FOSUserBundle which need to be available to anonymous users
+        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
+        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
+        # Secured part of the site
+        # This config requires being logged for the whole site and having the admin role for the admin part.
+        # Change these rules to adapt them to your needs
+        - { path: ^/packages/submit$, role: ROLE_USER }
+        - { path: ^/admin/, role: ROLE_ADMIN }
+
+    role_hierarchy:
+        ROLE_UPDATE_PACKAGES: ~
+        ROLE_DELETE_PACKAGES: ~
+        ROLE_EDIT_PACKAGES: ~
+
+        ROLE_ADMIN:       [ ROLE_USER, ROLE_UPDATE_PACKAGES, ROLE_EDIT_PACKAGES, ROLE_DELETE_PACKAGES ]
+        ROLE_SUPERADMIN:  [ ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
diff --git a/app/console b/app/console
new file mode 100755
index 0000000..b8bae1c
--- /dev/null
+++ b/app/console
@@ -0,0 +1,32 @@
+#!/usr/bin/env php
+<?php
+
+use Composer\Autoload\ClassLoader;
+use Symfony\Bundle\FrameworkBundle\Console\Application;
+use Symfony\Component\Console\Input\ArgvInput;
+use Symfony\Component\Debug\Debug;
+
+// if you don't want to setup permissions the proper way, just uncomment the following PHP line
+// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information
+//umask(0000);
+
+ini_set('date.timezone', 'UTC');
+
+set_time_limit(0);
+
+/**
+ * @var ClassLoader
+ */
+$loader = require __DIR__.'/autoload.php';
+
+$input = new ArgvInput();
+$env = $input->getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev');
+$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod';
+
+if ($debug) {
+    Debug::enable();
+}
+
+$kernel = new AppKernel($env, $debug);
+$application = new Application($kernel);
+$application->run($input);
diff --git a/app/java/compiler.jar b/app/java/compiler.jar
new file mode 100644
index 0000000..09ac825
Binary files /dev/null and b/app/java/compiler.jar differ
diff --git a/app/java/yuicompressor-2.4.2.jar b/app/java/yuicompressor-2.4.2.jar
new file mode 100644
index 0000000..c29470b
Binary files /dev/null and b/app/java/yuicompressor-2.4.2.jar differ
diff --git a/bin/.htaccess b/bin/.htaccess
new file mode 100644
index 0000000..3418e55
--- /dev/null
+++ b/bin/.htaccess
@@ -0,0 +1 @@
+deny from all
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..49bc577
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,99 @@
+{
+    "description": "Package Repository Website",
+    "keywords": ["package", "composer"],
+    "homepage": "http://packagist.org/",
+    "type": "library",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nils Adermann",
+            "email": "naderman@naderman.de",
+            "homepage": "http://www.naderman.de"
+        },
+        {
+            "name": "Jordi Boggiano",
+            "email": "j.boggiano@seld.be",
+            "homepage": "http://seld.be"
+        }
+    ],
+    "support": {
+          "email": "contact@packagist.org"
+    },
+    "autoload": {
+        "psr-4": {
+            "Packagist\\": "src/Packagist/",
+            "DrupalPackagist\\": "src/DrupalPackagist/"
+        },
+        "classmap": [ "app/AppKernel.php", "app/AppCache.php" ]
+    },
+    "require": {
+        "php": ">=5.6",
+        "symfony/symfony": "^2.8",
+        "doctrine/orm": "^2.4",
+        "doctrine/doctrine-bundle": "^1.2",
+        "twig/extensions": "^1.0",
+        "symfony/swiftmailer-bundle": "^2.3",
+        "symfony/monolog-bundle": "^2.4",
+        "sensio/distribution-bundle": "^5.0",
+        "sensio/framework-extra-bundle": "^3.0",
+        "sensio/generator-bundle": "^3.0",
+        "jms/security-extra-bundle": "^1.5",
+        "jms/di-extra-bundle": "^1.4",
+
+        "composer/composer": "^1.0@dev",
+        "friendsofsymfony/user-bundle": "^2.0@dev",
+        "hwi/oauth-bundle": "^0.4",
+        "nelmio/solarium-bundle": "^1.0",
+        "nelmio/security-bundle": "^1.0",
+        "predis/predis": "^1.0",
+        "snc/redis-bundle": "^1.1",
+        "white-october/pagerfanta-bundle": "^1.0",
+        "zendframework/zend-feed": "^2.0",
+        "zendframework/zend-servicemanager": "^2.0",
+        "zendframework/zend-uri": "^2.0",
+        "zendframework/zend-version": "^2.0",
+        "pagerfanta/pagerfanta": "^1.0",
+        "knplabs/knp-menu-bundle": "^2.1",
+        "ezyang/htmlpurifier": "^4.6",
+        "drupal/parse-composer": "dev-master@dev",
+        "php-amqplib/rabbitmq-bundle": "^1.9"
+    },
+    "_": ["fos user bundle 2.0.0 tag needed"],
+    "require-dev": {
+        "symfony/phpunit-bridge": "^2.7 || ^3.0"
+    },
+    "scripts": {
+        "post-install-cmd": [
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets"
+        ],
+        "post-update-cmd": [
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets"
+        ]
+    },
+    "extra": {
+        "symfony-app-dir": "app",
+        "symfony-web-dir": "web"
+    },
+    "repositories": [
+        {
+            "type": "git",
+            "url": "https://github.com/drupal-composer/drupal-parse-composer.git"
+        },
+        {
+            "type": "package",
+            "package": {
+                "name": "drupal/drupal",
+                "version": "7.41.0",
+                "dist":
+                {
+                    "url": "https://ftp.drupal.org/files/projects/drupal-7.41.zip",
+                    "type": "zip"
+                }
+            }
+        }
+    ]
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..96bf8ed
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,4546 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "hash": "8ceb8d836907efb216a68677ac3e21b5",
+    "content-hash": "247a1d51099616ff42130985d3fe0452",
+    "packages": [
+        {
+            "name": "composer/composer",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/composer.git",
+                "reference": "40c14709f79f9d7ea35ac969cfbd7f41beb525bb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/composer/zipball/40c14709f79f9d7ea35ac969cfbd7f41beb525bb",
+                "reference": "40c14709f79f9d7ea35ac969cfbd7f41beb525bb",
+                "shasum": ""
+            },
+            "require": {
+                "composer/semver": "^1.0",
+                "composer/spdx-licenses": "^1.0",
+                "justinrainbow/json-schema": "^1.6",
+                "php": "^5.3.2 || ^7.0",
+                "seld/cli-prompt": "^1.0",
+                "seld/jsonlint": "^1.4",
+                "seld/phar-utils": "^1.0",
+                "symfony/console": "^2.5 || ^3.0",
+                "symfony/filesystem": "^2.5 || ^3.0",
+                "symfony/finder": "^2.2 || ^3.0",
+                "symfony/process": "^2.1 || ^3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.5 || ^5.0.5",
+                "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+            },
+            "suggest": {
+                "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
+                "ext-zip": "Enabling the zip extension allows you to unzip archives",
+                "ext-zlib": "Allow gzip compression of HTTP requests"
+            },
+            "bin": [
+                "bin/composer"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\": "src/Composer"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
+            "homepage": "https://getcomposer.org/",
+            "keywords": [
+                "autoload",
+                "dependency",
+                "package"
+            ],
+            "time": "2016-04-05 12:27:16"
+        },
+        {
+            "name": "composer/semver",
+            "version": "1.4.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/semver.git",
+                "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573",
+                "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.2 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.5 || ^5.0.5",
+                "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Semver\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                },
+                {
+                    "name": "Rob Bast",
+                    "email": "rob.bast@gmail.com",
+                    "homepage": "http://robbast.nl"
+                }
+            ],
+            "description": "Semver library that offers utilities, version constraint parsing and validation.",
+            "keywords": [
+                "semantic",
+                "semver",
+                "validation",
+                "versioning"
+            ],
+            "time": "2016-08-30 16:08:34"
+        },
+        {
+            "name": "composer/spdx-licenses",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/spdx-licenses.git",
+                "reference": "547659c3cacd3ccfe1b4714c2ff88cafc6b6793b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/547659c3cacd3ccfe1b4714c2ff88cafc6b6793b",
+                "reference": "547659c3cacd3ccfe1b4714c2ff88cafc6b6793b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.2 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.5 || ^5.0.5",
+                "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Spdx\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                },
+                {
+                    "name": "Rob Bast",
+                    "email": "rob.bast@gmail.com",
+                    "homepage": "http://robbast.nl"
+                }
+            ],
+            "description": "SPDX licenses list and validation library.",
+            "keywords": [
+                "license",
+                "spdx",
+                "validator"
+            ],
+            "time": "2016-03-25 10:57:10"
+        },
+        {
+            "name": "container-interop/container-interop",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/container-interop/container-interop.git",
+                "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
+                "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Interop\\Container\\": "src/Interop/Container/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
+            "time": "2014-12-30 15:22:37"
+        },
+        {
+            "name": "doctrine/annotations",
+            "version": "v1.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/annotations.git",
+                "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
+                "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/lexer": "1.*",
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "doctrine/cache": "1.*",
+                "phpunit/phpunit": "4.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\Common\\Annotations\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Docblock Annotations Parser",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "annotations",
+                "docblock",
+                "parser"
+            ],
+            "time": "2015-08-31 12:32:49"
+        },
+        {
+            "name": "doctrine/cache",
+            "version": "v1.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/cache.git",
+                "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6",
+                "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6",
+                "shasum": ""
+            },
+            "require": {
+                "php": "~5.5|~7.0"
+            },
+            "conflict": {
+                "doctrine/common": ">2.2,<2.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8|~5.0",
+                "predis/predis": "~1.0",
+                "satooshi/php-coveralls": "~0.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Caching library offering an object-oriented API for many cache backends",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "cache",
+                "caching"
+            ],
+            "time": "2015-12-31 16:37:02"
+        },
+        {
+            "name": "doctrine/collections",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/collections.git",
+                "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a",
+                "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\Common\\Collections\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Collections Abstraction library",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "array",
+                "collections",
+                "iterator"
+            ],
+            "time": "2015-04-14 22:21:58"
+        },
+        {
+            "name": "doctrine/common",
+            "version": "v2.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/common.git",
+                "reference": "a579557bc689580c19fee4e27487a67fe60defc0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0",
+                "reference": "a579557bc689580c19fee4e27487a67fe60defc0",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/annotations": "1.*",
+                "doctrine/cache": "1.*",
+                "doctrine/collections": "1.*",
+                "doctrine/inflector": "1.*",
+                "doctrine/lexer": "1.*",
+                "php": "~5.5|~7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8|~5.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\": "lib/Doctrine/Common"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Common Library for Doctrine projects",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "annotations",
+                "collections",
+                "eventmanager",
+                "persistence",
+                "spl"
+            ],
+            "time": "2015-12-25 13:18:31"
+        },
+        {
+            "name": "doctrine/dbal",
+            "version": "v2.5.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/dbal.git",
+                "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/abbdfd1cff43a7b99d027af3be709bc8fc7d4769",
+                "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/common": ">=2.4,<2.7-dev",
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*",
+                "symfony/console": "2.*"
+            },
+            "suggest": {
+                "symfony/console": "For helpful console commands such as SQL execution and import of files."
+            },
+            "bin": [
+                "bin/doctrine-dbal"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.5.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\DBAL\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                }
+            ],
+            "description": "Database Abstraction Layer",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "database",
+                "dbal",
+                "persistence",
+                "queryobject"
+            ],
+            "time": "2016-01-05 22:11:12"
+        },
+        {
+            "name": "doctrine/doctrine-bundle",
+            "version": "1.6.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/DoctrineBundle.git",
+                "reference": "e9c2ccf573b59b7cea566390f34254fed3c20ed9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/e9c2ccf573b59b7cea566390f34254fed3c20ed9",
+                "reference": "e9c2ccf573b59b7cea566390f34254fed3c20ed9",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/dbal": "~2.3",
+                "doctrine/doctrine-cache-bundle": "~1.0",
+                "jdorn/sql-formatter": "~1.1",
+                "php": ">=5.3.2",
+                "symfony/console": "~2.3|~3.0",
+                "symfony/doctrine-bridge": "~2.2|~3.0",
+                "symfony/framework-bundle": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "doctrine/orm": "~2.3",
+                "phpunit/phpunit": "~4",
+                "satooshi/php-coveralls": "~0.6.1",
+                "symfony/phpunit-bridge": "~2.7|~3.0",
+                "symfony/validator": "~2.2|~3.0",
+                "symfony/yaml": "~2.2|~3.0",
+                "twig/twig": "~1.10"
+            },
+            "suggest": {
+                "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.",
+                "symfony/web-profiler-bundle": "to use the data collector"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Bundle\\DoctrineBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Doctrine Project",
+                    "homepage": "http://www.doctrine-project.org/"
+                },
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Symfony DoctrineBundle",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "database",
+                "dbal",
+                "orm",
+                "persistence"
+            ],
+            "time": "2016-01-10 17:21:44"
+        },
+        {
+            "name": "doctrine/doctrine-cache-bundle",
+            "version": "1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/DoctrineCacheBundle.git",
+                "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/DoctrineCacheBundle/zipball/18c600a9b82f6454d2e81ca4957cdd56a1cf3504",
+                "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/cache": "^1.4.2",
+                "doctrine/inflector": "~1.0",
+                "php": ">=5.3.2",
+                "symfony/doctrine-bridge": "~2.2|~3.0"
+            },
+            "require-dev": {
+                "instaclick/coding-standard": "~1.1",
+                "instaclick/object-calisthenics-sniffs": "dev-master",
+                "instaclick/symfony2-coding-standard": "dev-remaster",
+                "phpunit/phpunit": "~4",
+                "predis/predis": "~0.8",
+                "satooshi/php-coveralls": "~0.6.1",
+                "squizlabs/php_codesniffer": "~1.5",
+                "symfony/console": "~2.2|~3.0",
+                "symfony/finder": "~2.2|~3.0",
+                "symfony/framework-bundle": "~2.2|~3.0",
+                "symfony/phpunit-bridge": "~2.7|~3.0",
+                "symfony/security-acl": "~2.3|~3.0",
+                "symfony/validator": "~2.2|~3.0",
+                "symfony/yaml": "~2.2|~3.0"
+            },
+            "suggest": {
+                "symfony/security-acl": "For using this bundle to cache ACLs"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Bundle\\DoctrineCacheBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Fabio B. Silva",
+                    "email": "fabio.bat.silva@gmail.com"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@hotmail.com"
+                },
+                {
+                    "name": "Doctrine Project",
+                    "homepage": "http://www.doctrine-project.org/"
+                },
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Symfony Bundle for Doctrine Cache",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "cache",
+                "caching"
+            ],
+            "time": "2016-01-26 17:28:51"
+        },
+        {
+            "name": "doctrine/inflector",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "90b2128806bfde671b6952ab8bea493942c1fdae"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae",
+                "reference": "90b2128806bfde671b6952ab8bea493942c1fdae",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\Common\\Inflector\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Common String Manipulations with regard to casing and singular/plural rules.",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "inflection",
+                "pluralize",
+                "singularize",
+                "string"
+            ],
+            "time": "2015-11-06 14:35:42"
+        },
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3,<8.0-DEV"
+            },
+            "require-dev": {
+                "athletic/athletic": "~0.1.8",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpunit/phpunit": "~4.0",
+                "squizlabs/php_codesniffer": "~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "http://ocramius.github.com/"
+                }
+            ],
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://github.com/doctrine/instantiator",
+            "keywords": [
+                "constructor",
+                "instantiate"
+            ],
+            "time": "2015-06-14 21:17:01"
+        },
+        {
+            "name": "doctrine/lexer",
+            "version": "v1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/lexer.git",
+                "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c",
+                "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\Common\\Lexer\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "lexer",
+                "parser"
+            ],
+            "time": "2014-09-09 13:34:57"
+        },
+        {
+            "name": "doctrine/orm",
+            "version": "v2.5.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/doctrine2.git",
+                "reference": "bc4ddbfb0114cb33438cc811c9a740d8aa304aab"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/bc4ddbfb0114cb33438cc811c9a740d8aa304aab",
+                "reference": "bc4ddbfb0114cb33438cc811c9a740d8aa304aab",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/cache": "~1.4",
+                "doctrine/collections": "~1.2",
+                "doctrine/common": ">=2.5-dev,<2.7-dev",
+                "doctrine/dbal": ">=2.5-dev,<2.6-dev",
+                "doctrine/instantiator": "~1.0.1",
+                "ext-pdo": "*",
+                "php": ">=5.4",
+                "symfony/console": "~2.5|~3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0",
+                "symfony/yaml": "~2.3|~3.0"
+            },
+            "suggest": {
+                "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
+            },
+            "bin": [
+                "bin/doctrine",
+                "bin/doctrine.php"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\ORM\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                }
+            ],
+            "description": "Object-Relational-Mapper for PHP",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "database",
+                "orm"
+            ],
+            "time": "2016-01-05 21:34:58"
+        },
+        {
+            "name": "drupal/drupal",
+            "version": "7.41.0",
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/drupal-7.41.zip",
+                "reference": null,
+                "shasum": null
+            },
+            "type": "library"
+        },
+        {
+            "name": "drupal/parse-composer",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/drupal-composer/drupal-parse-composer.git",
+                "reference": "09f4f2f561ac178821b334f9627712fa48611c7f"
+            },
+            "require": {
+                "composer/semver": "^1.4",
+                "drupal/drupal": "7.*",
+                "guzzle/guzzle": "*",
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "composer/composer": "dev-master",
+                "leaphub/phpcs-symfony2-standard": "dev-master",
+                "phpspec/phpspec": "2.*",
+                "phpunit/phpunit": "4.4.*",
+                "squizlabs/php_codesniffer": "2.*",
+                "symfony/symfony": "2.3.*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Drupal\\ParseComposer\\": "src/"
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "authors": [
+                {
+                    "name": "Will Milton",
+                    "email": "wa.milton@gmail.com"
+                }
+            ],
+            "time": "2016-11-18 18:03:15"
+        },
+        {
+            "name": "ezyang/htmlpurifier",
+            "version": "v4.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ezyang/htmlpurifier.git",
+                "reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
+                "reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "HTMLPurifier": "library/"
+                },
+                "files": [
+                    "library/HTMLPurifier.composer.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL"
+            ],
+            "authors": [
+                {
+                    "name": "Edward Z. Yang",
+                    "email": "admin@htmlpurifier.org",
+                    "homepage": "http://ezyang.com"
+                }
+            ],
+            "description": "Standards compliant HTML filter written in PHP",
+            "homepage": "http://htmlpurifier.org/",
+            "keywords": [
+                "html"
+            ],
+            "time": "2015-08-05 01:03:42"
+        },
+        {
+            "name": "friendsofsymfony/user-bundle",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/FriendsOfSymfony/FOSUserBundle.git",
+                "reference": "a39d000577d735444bee97de9f5ee382e3f85fa1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/FriendsOfSymfony/FOSUserBundle/zipball/a39d000577d735444bee97de9f5ee382e3f85fa1",
+                "reference": "a39d000577d735444bee97de9f5ee382e3f85fa1",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.9",
+                "symfony/form": "~2.3|~3.0",
+                "symfony/framework-bundle": "~2.3|~3.0",
+                "symfony/security-bundle": "~2.3|~3.0",
+                "symfony/twig-bundle": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "doctrine/doctrine-bundle": "~1.3",
+                "swiftmailer/swiftmailer": "~4.3|~5",
+                "symfony/console": "~2.3|~3.0",
+                "symfony/phpunit-bridge": "~2.7|~3.0",
+                "symfony/validator": "~2.3|~3.0",
+                "symfony/yaml": "~2.3|~3.0",
+                "willdurand/propel-typehintable-behavior": "~1.0"
+            },
+            "suggest": {
+                "willdurand/propel-typehintable-behavior": "Needed when using the propel implementation"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "FOS\\UserBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christophe Coevoet",
+                    "email": "stof@notk.org"
+                },
+                {
+                    "name": "FriendsOfSymfony Community",
+                    "homepage": "https://github.com/friendsofsymfony/FOSUserBundle/contributors"
+                },
+                {
+                    "name": "Thibault Duplessis",
+                    "email": "thibault.duplessis@gmail.com"
+                }
+            ],
+            "description": "Symfony FOSUserBundle",
+            "homepage": "http://friendsofsymfony.github.com",
+            "keywords": [
+                "User management"
+            ],
+            "time": "2016-03-01 11:42:24"
+        },
+        {
+            "name": "guzzle/guzzle",
+            "version": "v3.9.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle3.git",
+                "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9",
+                "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-curl": "*",
+                "php": ">=5.3.3",
+                "symfony/event-dispatcher": "~2.1"
+            },
+            "replace": {
+                "guzzle/batch": "self.version",
+                "guzzle/cache": "self.version",
+                "guzzle/common": "self.version",
+                "guzzle/http": "self.version",
+                "guzzle/inflection": "self.version",
+                "guzzle/iterator": "self.version",
+                "guzzle/log": "self.version",
+                "guzzle/parser": "self.version",
+                "guzzle/plugin": "self.version",
+                "guzzle/plugin-async": "self.version",
+                "guzzle/plugin-backoff": "self.version",
+                "guzzle/plugin-cache": "self.version",
+                "guzzle/plugin-cookie": "self.version",
+                "guzzle/plugin-curlauth": "self.version",
+                "guzzle/plugin-error-response": "self.version",
+                "guzzle/plugin-history": "self.version",
+                "guzzle/plugin-log": "self.version",
+                "guzzle/plugin-md5": "self.version",
+                "guzzle/plugin-mock": "self.version",
+                "guzzle/plugin-oauth": "self.version",
+                "guzzle/service": "self.version",
+                "guzzle/stream": "self.version"
+            },
+            "require-dev": {
+                "doctrine/cache": "~1.3",
+                "monolog/monolog": "~1.0",
+                "phpunit/phpunit": "3.7.*",
+                "psr/log": "~1.0",
+                "symfony/class-loader": "~2.1",
+                "zendframework/zend-cache": "2.*,<2.3",
+                "zendframework/zend-log": "2.*,<2.3"
+            },
+            "suggest": {
+                "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Guzzle": "src/",
+                    "Guzzle\\Tests": "tests/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Guzzle Community",
+                    "homepage": "https://github.com/guzzle/guzzle/contributors"
+                }
+            ],
+            "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "rest",
+                "web service"
+            ],
+            "abandoned": "guzzlehttp/guzzle",
+            "time": "2015-03-18 18:23:50"
+        },
+        {
+            "name": "hwi/oauth-bundle",
+            "version": "0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/hwi/HWIOAuthBundle.git",
+                "reference": "86b5a876224b8a26707813aa94d3637886aa2041"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/hwi/HWIOAuthBundle/zipball/86b5a876224b8a26707813aa94d3637886aa2041",
+                "reference": "86b5a876224b8a26707813aa94d3637886aa2041",
+                "shasum": ""
+            },
+            "require": {
+                "kriswallsmith/buzz": "~0.13",
+                "php": ">=5.3.3",
+                "symfony/form": "~2.3",
+                "symfony/framework-bundle": "~2.3",
+                "symfony/options-resolver": "~2.3",
+                "symfony/security-bundle": "~2.3",
+                "symfony/yaml": "~2.3"
+            },
+            "conflict": {
+                "twig/twig": "<1.12"
+            },
+            "require-dev": {
+                "doctrine/orm": "~2.3",
+                "friendsofsymfony/user-bundle": "~1.3|~2.0",
+                "phpunit/phpunit": "~4.8",
+                "symfony/phpunit-bridge": "~2.7",
+                "symfony/property-access": "~2.3",
+                "symfony/twig-bundle": "~2.3",
+                "symfony/validator": "~2.3"
+            },
+            "suggest": {
+                "doctrine/doctrine-bundle": "to use Doctrine user provider",
+                "friendsofsymfony/user-bundle": "to connect FOSUB with this bundle",
+                "symfony/property-access": "to use FOSUB integration with this bundle",
+                "symfony/twig-bundle": "to use the Twig hwi_oauth_* functions"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "0.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "HWI\\Bundle\\OAuthBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Contributors",
+                    "homepage": "https://github.com/hwi/HWIOAuthBundle/contributors"
+                },
+                {
+                    "name": "Joseph Bielawski",
+                    "email": "stloyd@gmail.com"
+                },
+                {
+                    "name": "Alexander",
+                    "email": "iam.asm89@gmail.com"
+                },
+                {
+                    "name": "Geoffrey Bachelet",
+                    "email": "geoffrey.bachelet@gmail.com"
+                }
+            ],
+            "description": "Support for authenticating users using both OAuth1.0a and OAuth2 in Symfony2.",
+            "homepage": "http://github.com/hwi/HWIOAuthBundle",
+            "keywords": [
+                "37signals",
+                "Authentication",
+                "Deezer",
+                "EVE Online",
+                "amazon",
+                "auth0",
+                "azure",
+                "bitbucket",
+                "bitly",
+                "box",
+                "bufferapp",
+                "dailymotion",
+                "deviantart",
+                "discogs",
+                "disqus",
+                "dropbox",
+                "eventbrite",
+                "facebook",
+                "firewall",
+                "fiware",
+                "flickr",
+                "foursquare",
+                "github",
+                "google",
+                "hubic",
+                "instagram",
+                "jira",
+                "linkedin",
+                "mail.ru",
+                "oauth",
+                "oauth1",
+                "oauth2",
+                "odnoklassniki",
+                "paypal",
+                "qq",
+                "reddit",
+                "runkeeper",
+                "salesforce",
+                "security",
+                "sensio connect",
+                "sina weibo",
+                "slack",
+                "sound cloud",
+                "spotify",
+                "stack exchange",
+                "stereomood",
+                "strava",
+                "toshl",
+                "trakt",
+                "trello",
+                "twitch",
+                "twitter",
+                "vkontakte",
+                "wechat",
+                "windows live",
+                "wordpress",
+                "xing",
+                "yahoo",
+                "yandex",
+                "youtube"
+            ],
+            "time": "2015-12-04 12:33:38"
+        },
+        {
+            "name": "ircmaxell/password-compat",
+            "version": "v1.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ircmaxell/password_compat.git",
+                "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c",
+                "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c",
+                "shasum": ""
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "lib/password.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Anthony Ferrara",
+                    "email": "ircmaxell@php.net",
+                    "homepage": "http://blog.ircmaxell.com"
+                }
+            ],
+            "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash",
+            "homepage": "https://github.com/ircmaxell/password_compat",
+            "keywords": [
+                "hashing",
+                "password"
+            ],
+            "time": "2014-11-20 16:49:30"
+        },
+        {
+            "name": "jdorn/sql-formatter",
+            "version": "v1.2.17",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jdorn/sql-formatter.git",
+                "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc",
+                "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "3.7.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "lib"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jeremy Dorn",
+                    "email": "jeremy@jeremydorn.com",
+                    "homepage": "http://jeremydorn.com/"
+                }
+            ],
+            "description": "a PHP SQL highlighting library",
+            "homepage": "https://github.com/jdorn/sql-formatter/",
+            "keywords": [
+                "highlight",
+                "sql"
+            ],
+            "time": "2014-01-12 16:20:24"
+        },
+        {
+            "name": "jms/aop-bundle",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/JMSAopBundle.git",
+                "reference": "78000d007e74283cc564a58e184d7f62548ad394"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/JMSAopBundle/zipball/78000d007e74283cc564a58e184d7f62548ad394",
+                "reference": "78000d007e74283cc564a58e184d7f62548ad394",
+                "shasum": ""
+            },
+            "require": {
+                "jms/cg": "^1.1",
+                "php": ">=5.3.9",
+                "symfony/framework-bundle": "^2.3|^3.0"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^2.7"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "JMS\\AopBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Adds AOP capabilities to Symfony2",
+            "keywords": [
+                "annotations",
+                "aop"
+            ],
+            "time": "2015-12-09 16:30:46"
+        },
+        {
+            "name": "jms/cg",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/cg-library.git",
+                "reference": "0af1113c7409b8636c5244bbae10b2e0ff792e9c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/cg-library/zipball/0af1113c7409b8636c5244bbae10b2e0ff792e9c",
+                "reference": "0af1113c7409b8636c5244bbae10b2e0ff792e9c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "CG\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache2"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Toolset for generating PHP code",
+            "keywords": [
+                "code generation"
+            ],
+            "time": "2015-09-13 08:54:43"
+        },
+        {
+            "name": "jms/di-extra-bundle",
+            "version": "1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/JMSDiExtraBundle.git",
+                "reference": "85a989bedcf22495c03bf94339ddc5e856bd9b46"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/JMSDiExtraBundle/zipball/85a989bedcf22495c03bf94339ddc5e856bd9b46",
+                "reference": "85a989bedcf22495c03bf94339ddc5e856bd9b46",
+                "shasum": ""
+            },
+            "require": {
+                "jms/aop-bundle": "~1.1",
+                "jms/metadata": "~1.0",
+                "symfony/dependency-injection": "~2.3|~3.0",
+                "symfony/finder": "~2.3|~3.0",
+                "symfony/framework-bundle": "~2.3|~3.0",
+                "symfony/http-kernel": "^2.3.24|~3.0",
+                "symfony/process": "~2.3|~3.0",
+                "symfony/routing": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "doctrine/doctrine-bundle": "~1.5",
+                "doctrine/orm": "~2.3",
+                "jms/security-extra-bundle": "~1.0@dev",
+                "phpcollection/phpcollection": ">=0.2,<0.3-dev",
+                "sensio/framework-extra-bundle": "~2.0|~3.0",
+                "symfony/browser-kit": "~2.3|~3.0",
+                "symfony/class-loader": "~2.3|~3.0",
+                "symfony/form": "~2.3|~3.0",
+                "symfony/phpunit-bridge": "~2.7",
+                "symfony/security-bundle": "~2.3",
+                "symfony/twig-bundle": "~2.3|~3.0",
+                "symfony/validator": "~2.3|~3.0",
+                "symfony/yaml": "~2.3|~3.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "JMS\\DiExtraBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Allows to configure dependency injection using annotations",
+            "homepage": "http://jmsyst.com/bundles/JMSDiExtraBundle",
+            "keywords": [
+                "annotations",
+                "dependency injection"
+            ],
+            "time": "2016-02-21 22:14:09"
+        },
+        {
+            "name": "jms/metadata",
+            "version": "1.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/metadata.git",
+                "reference": "22b72455559a25777cfd28c4ffda81ff7639f353"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/22b72455559a25777cfd28c4ffda81ff7639f353",
+                "reference": "22b72455559a25777cfd28c4ffda81ff7639f353",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "doctrine/cache": "~1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.5.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Metadata\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com",
+                    "homepage": "https://github.com/schmittjoh",
+                    "role": "Developer of wrapped JMSSerializerBundle"
+                }
+            ],
+            "description": "Class/method/property metadata management in PHP",
+            "keywords": [
+                "annotations",
+                "metadata",
+                "xml",
+                "yaml"
+            ],
+            "time": "2014-07-12 07:13:19"
+        },
+        {
+            "name": "jms/parser-lib",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/parser-lib.git",
+                "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d",
+                "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d",
+                "shasum": ""
+            },
+            "require": {
+                "phpoption/phpoption": ">=0.9,<2.0-dev"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "JMS\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache2"
+            ],
+            "description": "A library for easily creating recursive-descent parsers.",
+            "time": "2012-11-18 18:08:43"
+        },
+        {
+            "name": "jms/security-extra-bundle",
+            "version": "1.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/JMSSecurityExtraBundle.git",
+                "reference": "d70399b25f41afd8277335cedc3c9b3acdc3759d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/JMSSecurityExtraBundle/zipball/d70399b25f41afd8277335cedc3c9b3acdc3759d",
+                "reference": "d70399b25f41afd8277335cedc3c9b3acdc3759d",
+                "shasum": ""
+            },
+            "require": {
+                "jms/aop-bundle": "~1.0",
+                "jms/di-extra-bundle": "~1.4",
+                "jms/metadata": "~1.0",
+                "jms/parser-lib": "~1.0",
+                "php": "~5.3|~7.0",
+                "symfony/dependency-injection": "~2.3|~3.0",
+                "symfony/framework-bundle": "~2.3|~3.0",
+                "symfony/routing": "~2.3|~3.0",
+                "symfony/security-acl": "~2.2|~3.0",
+                "symfony/security-bundle": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "doctrine/common": "~2.3",
+                "doctrine/doctrine-bundle": "~1.0",
+                "doctrine/orm": "~2.3",
+                "sensio/framework-extra-bundle": "^2.2.1|~3.0",
+                "symfony/browser-kit": "~2.0|~3.0",
+                "symfony/class-loader": "~2.1|~3.0",
+                "symfony/css-selector": "^2.0.5|~3.0",
+                "symfony/finder": "^2.0.5|~3.0",
+                "symfony/form": "~2.2|~3.0",
+                "symfony/phpunit-bridge": "~2.7|~3.0",
+                "symfony/process": "^2.0.5|~3.0",
+                "symfony/twig-bridge": "^2.2.6|~3.0",
+                "symfony/twig-bundle": "~2.2|~3.0",
+                "symfony/validator": "~2.2|~3.0",
+                "symfony/yaml": "^2.2.11|~3.0",
+                "twig/twig": "^1.23.1"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "JMS\\SecurityExtraBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache2"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Enhances the Symfony2 Security Component by adding several new features",
+            "homepage": "http://jmsyst.com/bundles/JMSSecurityExtraBundle",
+            "keywords": [
+                "annotations",
+                "authorization",
+                "expression",
+                "secure",
+                "security"
+            ],
+            "time": "2016-02-03 15:29:16"
+        },
+        {
+            "name": "justinrainbow/json-schema",
+            "version": "1.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/justinrainbow/json-schema.git",
+                "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341",
+                "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.29"
+            },
+            "require-dev": {
+                "json-schema/json-schema-test-suite": "1.1.0",
+                "phpdocumentor/phpdocumentor": "~2",
+                "phpunit/phpunit": "~3.7"
+            },
+            "bin": [
+                "bin/validate-json"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "JsonSchema\\": "src/JsonSchema/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Bruno Prieto Reis",
+                    "email": "bruno.p.reis@gmail.com"
+                },
+                {
+                    "name": "Justin Rainbow",
+                    "email": "justin.rainbow@gmail.com"
+                },
+                {
+                    "name": "Igor Wiedler",
+                    "email": "igor@wiedler.ch"
+                },
+                {
+                    "name": "Robert Schönthal",
+                    "email": "seroscho@googlemail.com"
+                }
+            ],
+            "description": "A library to validate a json schema.",
+            "homepage": "https://github.com/justinrainbow/json-schema",
+            "keywords": [
+                "json",
+                "schema"
+            ],
+            "time": "2016-01-25 15:43:01"
+        },
+        {
+            "name": "knplabs/knp-menu",
+            "version": "v2.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/KnpLabs/KnpMenu.git",
+                "reference": "9917b999a3c3d3901386d60c4888b07679291031"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/KnpLabs/KnpMenu/zipball/9917b999a3c3d3901386d60c4888b07679291031",
+                "reference": "9917b999a3c3d3901386d60c4888b07679291031",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "pimple/pimple": "~1.0",
+                "silex/silex": "~1.0",
+                "symfony/phpunit-bridge": "~2.7|~3.0",
+                "symfony/routing": "~2.3|~3.0",
+                "twig/twig": "~1.16|~2.0"
+            },
+            "suggest": {
+                "pimple/pimple": "for the built-in implementations of the menu provider and renderer provider",
+                "silex/silex": "for the integration with your silex application",
+                "twig/twig": "for the TwigRenderer and the integration with your templates"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Knp\\Menu\\": "src/Knp/Menu"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christophe Coevoet",
+                    "email": "stof@notk.org"
+                },
+                {
+                    "name": "KnpLabs",
+                    "homepage": "http://knplabs.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://github.com/KnpLabs/KnpMenu/contributors"
+                }
+            ],
+            "description": "An object oriented menu library",
+            "homepage": "http://knplabs.com",
+            "keywords": [
+                "menu",
+                "tree"
+            ],
+            "time": "2016-01-08 15:42:54"
+        },
+        {
+            "name": "knplabs/knp-menu-bundle",
+            "version": "v2.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/KnpLabs/KnpMenuBundle.git",
+                "reference": "90aff8e39274d1225dfa3bb7b1dd4e47b7312dca"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/KnpLabs/KnpMenuBundle/zipball/90aff8e39274d1225dfa3bb7b1dd4e47b7312dca",
+                "reference": "90aff8e39274d1225dfa3bb7b1dd4e47b7312dca",
+                "shasum": ""
+            },
+            "require": {
+                "knplabs/knp-menu": "~2.1",
+                "symfony/framework-bundle": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "symfony/expression-language": "~2.4|~3.0",
+                "symfony/phpunit-bridge": "~2.7|~3.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Knp\\Bundle\\MenuBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christophe Coevoet",
+                    "email": "stof@notk.org"
+                },
+                {
+                    "name": "Knplabs",
+                    "homepage": "http://knplabs.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://github.com/KnpLabs/KnpMenuBundle/contributors"
+                }
+            ],
+            "description": "This bundle provides an integration of the KnpMenu library",
+            "keywords": [
+                "menu"
+            ],
+            "time": "2015-12-15 12:06:23"
+        },
+        {
+            "name": "kriswallsmith/buzz",
+            "version": "v0.15",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/kriswallsmith/Buzz.git",
+                "reference": "d4041666c3ffb379af02a92dabe81c904b35fab8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/d4041666c3ffb379af02a92dabe81c904b35fab8",
+                "reference": "d4041666c3ffb379af02a92dabe81c904b35fab8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "3.7.*"
+            },
+            "suggest": {
+                "ext-curl": "*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Buzz": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kris Wallsmith",
+                    "email": "kris.wallsmith@gmail.com",
+                    "homepage": "http://kriswallsmith.net/"
+                }
+            ],
+            "description": "Lightweight HTTP client",
+            "homepage": "https://github.com/kriswallsmith/Buzz",
+            "keywords": [
+                "curl",
+                "http client"
+            ],
+            "time": "2015-06-25 17:26:56"
+        },
+        {
+            "name": "monolog/monolog",
+            "version": "1.17.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/monolog.git",
+                "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24",
+                "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "psr/log": "~1.0"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0.0"
+            },
+            "require-dev": {
+                "aws/aws-sdk-php": "^2.4.9",
+                "doctrine/couchdb": "~1.0@dev",
+                "graylog2/gelf-php": "~1.0",
+                "jakub-onderka/php-parallel-lint": "0.9",
+                "php-console/php-console": "^3.1.3",
+                "phpunit/phpunit": "~4.5",
+                "phpunit/phpunit-mock-objects": "2.3.0",
+                "raven/raven": "^0.13",
+                "ruflin/elastica": ">=0.90 <3.0",
+                "swiftmailer/swiftmailer": "~5.3",
+                "videlalvaro/php-amqplib": "~2.4"
+            },
+            "suggest": {
+                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+                "ext-mongo": "Allow sending log messages to a MongoDB server",
+                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+                "php-console/php-console": "Allow sending log messages to Google Chrome",
+                "raven/raven": "Allow sending log messages to a Sentry server",
+                "rollbar/rollbar": "Allow sending log messages to Rollbar",
+                "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
+                "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.16.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Monolog\\": "src/Monolog"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+            "homepage": "http://github.com/Seldaek/monolog",
+            "keywords": [
+                "log",
+                "logging",
+                "psr-3"
+            ],
+            "time": "2015-10-14 12:51:02"
+        },
+        {
+            "name": "nelmio/security-bundle",
+            "version": "1.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nelmio/NelmioSecurityBundle.git",
+                "reference": "4be243f1fc85ff85f10aadcf88c8c11ba2096cd9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nelmio/NelmioSecurityBundle/zipball/4be243f1fc85ff85f10aadcf88c8c11ba2096cd9",
+                "reference": "4be243f1fc85ff85f10aadcf88c8c11ba2096cd9",
+                "shasum": ""
+            },
+            "require": {
+                "symfony/framework-bundle": "~2.3|~3.0",
+                "symfony/security": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.2"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.10.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Nelmio\\SecurityBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nelmio",
+                    "homepage": "http://nelm.io"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://github.com/nelmio/NelmioSecurityBundle/contributors"
+                }
+            ],
+            "description": "Extra security-related features for Symfony: signed/encrypted cookies, HTTPS/SSL/HSTS handling, cookie session storage, ...",
+            "keywords": [
+                "security"
+            ],
+            "time": "2016-02-23 10:42:13"
+        },
+        {
+            "name": "nelmio/solarium-bundle",
+            "version": "v1.1.0",
+            "target-dir": "Nelmio/SolariumBundle",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nelmio/NelmioSolariumBundle.git",
+                "reference": "693700c4deeb04997b90aca659dd881409f33eb9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nelmio/NelmioSolariumBundle/zipball/693700c4deeb04997b90aca659dd881409f33eb9",
+                "reference": "693700c4deeb04997b90aca659dd881409f33eb9",
+                "shasum": ""
+            },
+            "require": {
+                "solarium/solarium": "~2.4.0",
+                "symfony/framework-bundle": "2.*"
+            },
+            "require-dev": {
+                "symfony/yaml": "2.*"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Nelmio\\SolariumBundle": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nelmio",
+                    "homepage": "http://nelm.io"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://github.com/nelmio/NelmioSolariumBundle/contributors"
+                }
+            ],
+            "description": "Integration with solarium solr client.",
+            "keywords": [
+                "search",
+                "solarium",
+                "solr"
+            ],
+            "time": "2013-01-07 10:35:43"
+        },
+        {
+            "name": "pagerfanta/pagerfanta",
+            "version": "v1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/whiteoctober/Pagerfanta.git",
+                "reference": "a874d3612d954dcbbb49e5ffe178890918fb76fb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/a874d3612d954dcbbb49e5ffe178890918fb76fb",
+                "reference": "a874d3612d954dcbbb49e5ffe178890918fb76fb",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "doctrine/orm": "~2.3",
+                "doctrine/phpcr-odm": "1.*",
+                "jackalope/jackalope-doctrine-dbal": "1.*",
+                "jmikola/geojson": "~1.0",
+                "mandango/mandango": "~1.0@dev",
+                "mandango/mondator": "~1.0@dev",
+                "phpunit/phpunit": "~4",
+                "propel/propel1": "~1.6",
+                "ruflin/elastica": "~1.3",
+                "solarium/solarium": "~3.1"
+            },
+            "suggest": {
+                "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.",
+                "doctrine/orm": "To use the DoctrineORMAdapter.",
+                "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0",
+                "mandango/mandango": "To use the MandangoAdapter.",
+                "propel/propel1": "To use the PropelAdapter",
+                "solarium/solarium": "To use the SolariumAdapter."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Pagerfanta\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Pablo Díez",
+                    "email": "pablodip@gmail.com"
+                }
+            ],
+            "description": "Pagination for PHP 5.3",
+            "keywords": [
+                "page",
+                "pagination",
+                "paginator",
+                "paging"
+            ],
+            "time": "2014-10-06 10:57:25"
+        },
+        {
+            "name": "paragonie/random_compat",
+            "version": "v1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/paragonie/random_compat.git",
+                "reference": "f078eba3bcf140fd69b5fcc3ea5ac809abf729dc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/paragonie/random_compat/zipball/f078eba3bcf140fd69b5fcc3ea5ac809abf729dc",
+                "reference": "f078eba3bcf140fd69b5fcc3ea5ac809abf729dc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*|5.*"
+            },
+            "suggest": {
+                "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "lib/random.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paragon Initiative Enterprises",
+                    "email": "security@paragonie.com",
+                    "homepage": "https://paragonie.com"
+                }
+            ],
+            "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+            "keywords": [
+                "csprng",
+                "pseudorandom",
+                "random"
+            ],
+            "time": "2016-02-29 17:25:04"
+        },
+        {
+            "name": "php-amqplib/php-amqplib",
+            "version": "v2.6.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-amqplib/php-amqplib.git",
+                "reference": "ba9468b08b5ff90859ef5069a6b9a70bcdd98fa1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/ba9468b08b5ff90859ef5069a6b9a70bcdd98fa1",
+                "reference": "ba9468b08b5ff90859ef5069a6b9a70bcdd98fa1",
+                "shasum": ""
+            },
+            "require": {
+                "ext-bcmath": "*",
+                "ext-mbstring": "*",
+                "php": ">=5.3.0"
+            },
+            "replace": {
+                "videlalvaro/php-amqplib": "self.version"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8",
+                "scrutinizer/ocular": "^1.1"
+            },
+            "suggest": {
+                "ext-sockets": "Use AMQPSocketConnection"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpAmqpLib\\": "PhpAmqpLib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1"
+            ],
+            "authors": [
+                {
+                    "name": "Alvaro Videla",
+                    "role": "Original Maintainer"
+                },
+                {
+                    "name": "John Kelly",
+                    "email": "johnmkelly86@gmail.com",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Raúl Araya",
+                    "email": "nubeiro@gmail.com",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "Formerly videlalvaro/php-amqplib.  This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
+            "homepage": "https://github.com/php-amqplib/php-amqplib/",
+            "keywords": [
+                "message",
+                "queue",
+                "rabbitmq"
+            ],
+            "time": "2016-03-02 16:36:48"
+        },
+        {
+            "name": "php-amqplib/rabbitmq-bundle",
+            "version": "v1.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-amqplib/RabbitMqBundle.git",
+                "reference": "09be4bea8f84613946f79d01b95c2828320eb670"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-amqplib/RabbitMqBundle/zipball/09be4bea8f84613946f79d01b95c2828320eb670",
+                "reference": "09be4bea8f84613946f79d01b95c2828320eb670",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "php-amqplib/php-amqplib": "~2.6",
+                "symfony/config": "~2.3 || ~3.0",
+                "symfony/console": "~2.3 || ~3.0",
+                "symfony/dependency-injection": "~2.3 || ~3.0",
+                "symfony/event-dispatcher": "~2.3 || ~3.0",
+                "symfony/yaml": "~2.3 || ~3.0"
+            },
+            "replace": {
+                "oldsound/rabbitmq-bundle": "self.version"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8 || ~5.0",
+                "symfony/debug": "~2.3 || ~3.0",
+                "symfony/serializer": "~2.3 || ~3.0"
+            },
+            "suggest": {
+                "symfony/framework-bundle": "To use this lib as a full Symfony Bundle and to use the profiler data collector"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.9.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "OldSound\\RabbitMqBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Alvaro Videla"
+                }
+            ],
+            "description": "Integrates php-amqplib with Symfony2 and RabbitMq. Formerly oldsound/rabbitmq-bundle.",
+            "keywords": [
+                "AMQP",
+                "Symfony2",
+                "message",
+                "queue",
+                "rabbitmq"
+            ],
+            "time": "2016-03-03 09:00:12"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed",
+                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.7.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "PhpOption\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache2"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "time": "2015-07-25 16:39:46"
+        },
+        {
+            "name": "predis/predis",
+            "version": "v1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nrk/predis.git",
+                "reference": "84060b9034d756b4d79641667d7f9efe1aeb8e04"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nrk/predis/zipball/84060b9034d756b4d79641667d7f9efe1aeb8e04",
+                "reference": "84060b9034d756b4d79641667d7f9efe1aeb8e04",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0"
+            },
+            "suggest": {
+                "ext-curl": "Allows access to Webdis when paired with phpiredis",
+                "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Predis\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Daniele Alessandri",
+                    "email": "suppakilla@gmail.com",
+                    "homepage": "http://clorophilla.net"
+                }
+            ],
+            "description": "Flexible and feature-complete PHP client library for Redis",
+            "homepage": "http://github.com/nrk/predis",
+            "keywords": [
+                "nosql",
+                "predis",
+                "redis"
+            ],
+            "time": "2015-07-30 18:34:15"
+        },
+        {
+            "name": "psr/log",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
+                "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Psr\\Log\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "time": "2012-12-21 11:40:51"
+        },
+        {
+            "name": "seld/cli-prompt",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/cli-prompt.git",
+                "reference": "b27db1514f7d7bb7a366ad95d4eb2b17140a0691"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/b27db1514f7d7bb7a366ad95d4eb2b17140a0691",
+                "reference": "b27db1514f7d7bb7a366ad95d4eb2b17140a0691",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Seld\\CliPrompt\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be"
+                }
+            ],
+            "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type",
+            "keywords": [
+                "cli",
+                "console",
+                "hidden",
+                "input",
+                "prompt"
+            ],
+            "time": "2016-01-09 17:55:27"
+        },
+        {
+            "name": "seld/jsonlint",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/jsonlint.git",
+                "reference": "66834d3e3566bb5798db7294619388786ae99394"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394",
+                "reference": "66834d3e3566bb5798db7294619388786ae99394",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3 || ^7.0"
+            },
+            "bin": [
+                "bin/jsonlint"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Seld\\JsonLint\\": "src/Seld/JsonLint/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "JSON Linter",
+            "keywords": [
+                "json",
+                "linter",
+                "parser",
+                "validator"
+            ],
+            "time": "2015-11-21 02:21:41"
+        },
+        {
+            "name": "seld/phar-utils",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/phar-utils.git",
+                "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a",
+                "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Seld\\PharUtils\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be"
+                }
+            ],
+            "description": "PHAR file format utilities, for when PHP phars you up",
+            "keywords": [
+                "phra"
+            ],
+            "time": "2015-10-13 18:44:15"
+        },
+        {
+            "name": "sensio/distribution-bundle",
+            "version": "v5.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sensiolabs/SensioDistributionBundle.git",
+                "reference": "2c167426fc24f9de116345195bc1697a748ee847"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/2c167426fc24f9de116345195bc1697a748ee847",
+                "reference": "2c167426fc24f9de116345195bc1697a748ee847",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.9",
+                "sensiolabs/security-checker": "~3.0",
+                "symfony/class-loader": "~2.3|~3.0",
+                "symfony/config": "~2.3|~3.0",
+                "symfony/dependency-injection": "~2.3|~3.0",
+                "symfony/filesystem": "~2.3|~3.0",
+                "symfony/http-kernel": "~2.3|~3.0",
+                "symfony/process": "~2.3|~3.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Sensio\\Bundle\\DistributionBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Base bundle for Symfony Distributions",
+            "keywords": [
+                "configuration",
+                "distribution"
+            ],
+            "time": "2016-02-12 16:21:30"
+        },
+        {
+            "name": "sensio/framework-extra-bundle",
+            "version": "v3.0.14",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
+                "reference": "cccf975c565ccd835bddc30a8fea5cdfe3357bf1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/cccf975c565ccd835bddc30a8fea5cdfe3357bf1",
+                "reference": "cccf975c565ccd835bddc30a8fea5cdfe3357bf1",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/common": "~2.2",
+                "symfony/dependency-injection": "~2.3|~3.0",
+                "symfony/framework-bundle": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "symfony/browser-kit": "~2.3|~3.0",
+                "symfony/dom-crawler": "~2.3|~3.0",
+                "symfony/expression-language": "~2.4|~3.0",
+                "symfony/finder": "~2.3|~3.0",
+                "symfony/phpunit-bridge": "~2.7|~3.0",
+                "symfony/security-bundle": "~2.4|~3.0",
+                "symfony/twig-bundle": "~2.3|~3.0",
+                "twig/twig": "~1.11|~2.0"
+            },
+            "suggest": {
+                "symfony/expression-language": "",
+                "symfony/psr-http-message-bridge": "To use the PSR-7 converters",
+                "symfony/security-bundle": ""
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Sensio\\Bundle\\FrameworkExtraBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "This bundle provides a way to configure your controllers with annotations",
+            "keywords": [
+                "annotations",
+                "controllers"
+            ],
+            "time": "2016-03-01 10:50:07"
+        },
+        {
+            "name": "sensio/generator-bundle",
+            "version": "v3.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sensiolabs/SensioGeneratorBundle.git",
+                "reference": "ac91535054d025937d897d78ebb5fc2da5e955a4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sensiolabs/SensioGeneratorBundle/zipball/ac91535054d025937d897d78ebb5fc2da5e955a4",
+                "reference": "ac91535054d025937d897d78ebb5fc2da5e955a4",
+                "shasum": ""
+            },
+            "require": {
+                "symfony/console": "~2.7|~3.0",
+                "symfony/framework-bundle": "~2.7|~3.0",
+                "symfony/process": "~2.7|~3.0",
+                "symfony/yaml": "~2.7|~3.0"
+            },
+            "require-dev": {
+                "doctrine/orm": "~2.4",
+                "symfony/doctrine-bridge": "~2.7|~3.0",
+                "twig/twig": "~1.18"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Sensio\\Bundle\\GeneratorBundle\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "This bundle generates code for you",
+            "time": "2016-02-26 04:36:01"
+        },
+        {
+            "name": "sensiolabs/security-checker",
+            "version": "v3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sensiolabs/security-checker.git",
+                "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/21696b0daa731064c23cfb694c60a2584a7b6e93",
+                "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93",
+                "shasum": ""
+            },
+            "require": {
+                "symfony/console": "~2.0|~3.0"
+            },
+            "bin": [
+                "security-checker"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "SensioLabs\\Security": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien.potencier@gmail.com"
+                }
+            ],
+            "description": "A security checker for your composer.lock",
+            "time": "2015-11-07 08:07:40"
+        },
+        {
+            "name": "snc/redis-bundle",
+            "version": "1.1.10",
+            "target-dir": "Snc/RedisBundle",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/snc/SncRedisBundle.git",
+                "reference": "3b11bdc877f470fa8a1e728ada48c62c0d33e0ed"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/snc/SncRedisBundle/zipball/3b11bdc877f470fa8a1e728ada48c62c0d33e0ed",
+                "reference": "3b11bdc877f470fa8a1e728ada48c62c0d33e0ed",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/framework-bundle": "^2.1",
+                "symfony/yaml": "^2.1"
+            },
+            "require-dev": {
+                "doctrine/cache": "^1.0",
+                "phpunit/phpunit": "^4.0",
+                "predis/predis": "^1.0",
+                "symfony/console": "^2.1"
+            },
+            "suggest": {
+                "monolog/monolog": "If you want to use the monolog redis handler.",
+                "predis/predis": "If you want to use predis.",
+                "symfony/console": "If you want to use commands to interact with the redis database"
+            },
+            "type": "symfony-bundle",
+            "autoload": {
+                "psr-0": {
+                    "Snc\\RedisBundle": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Henrik Westphal",
+                    "email": "henrik.westphal@gmail.com"
+                },
+                {
+                    "name": "Community contributors",
+                    "homepage": "https://github.com/snc/SncRedisBundle/contributors"
+                }
+            ],
+            "description": "A Redis bundle for Symfony2",
+            "homepage": "https://github.com/snc/SncRedisBundle",
+            "keywords": [
+                "nosql",
+                "redis",
+                "symfony"
+            ],
+            "time": "2016-01-21 18:29:37"
+        },
+        {
+            "name": "solarium/solarium",
+            "version": "2.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/solariumphp/solarium.git",
+                "reference": "f7c55cf42d14bb70f534128da3e343bb98fcb504"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/solariumphp/solarium/zipball/f7c55cf42d14bb70f534128da3e343bb98fcb504",
+                "reference": "f7c55cf42d14bb70f534128da3e343bb98fcb504",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Solarium": "library/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "NewBSD"
+            ],
+            "authors": [
+                {
+                    "name": "See GitHub contributors",
+                    "homepage": "https://github.com/basdenooijer/solarium/contributors"
+                }
+            ],
+            "description": "PHP Solr client",
+            "homepage": "http://www.solarium-project.org",
+            "keywords": [
+                "php",
+                "search",
+                "solr"
+            ],
+            "time": "2013-02-11 13:12:43"
+        },
+        {
+            "name": "swiftmailer/swiftmailer",
+            "version": "v5.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/swiftmailer/swiftmailer.git",
+                "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421",
+                "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "mockery/mockery": "~0.9.1,<0.9.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.4-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "lib/swift_required.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Chris Corbyn"
+                },
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Swiftmailer, free feature-rich PHP mailer",
+            "homepage": "http://swiftmailer.org",
+            "keywords": [
+                "email",
+                "mail",
+                "mailer"
+            ],
+            "time": "2015-06-06 14:19:39"
+        },
+        {
+            "name": "symfony/monolog-bundle",
+            "version": "v2.8.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/monolog-bundle.git",
+                "reference": "84785c4d44801c4dd82829fa2e1820cacfe2c46f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/84785c4d44801c4dd82829fa2e1820cacfe2c46f",
+                "reference": "84785c4d44801c4dd82829fa2e1820cacfe2c46f",
+                "shasum": ""
+            },
+            "require": {
+                "monolog/monolog": "~1.8",
+                "php": ">=5.3.2",
+                "symfony/config": "~2.3|~3.0",
+                "symfony/dependency-injection": "~2.3|~3.0",
+                "symfony/http-kernel": "~2.3|~3.0",
+                "symfony/monolog-bridge": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "symfony/console": "~2.3|~3.0",
+                "symfony/yaml": "~2.3|~3.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.8.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Bundle\\MonologBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Symfony MonologBundle",
+            "homepage": "http://symfony.com",
+            "keywords": [
+                "log",
+                "logging"
+            ],
+            "time": "2015-11-17 10:02:29"
+        },
+        {
+            "name": "symfony/polyfill-apcu",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-apcu.git",
+                "reference": "d1911e6caeb4b6a4c8e2d5c46b978a66b3745e4c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/d1911e6caeb4b6a4c8e2d5c46b978a66b3745e4c",
+                "reference": "d1911e6caeb4b6a4c8e2d5c46b978a66b3745e4c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting apcu_* functions to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "apcu",
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2016-01-20 09:13:37"
+        },
+        {
+            "name": "symfony/polyfill-intl-icu",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-icu.git",
+                "reference": "66b0bb4abda229bc073eff6bbc8f2685bdaac165"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/66b0bb4abda229bc073eff6bbc8f2685bdaac165",
+                "reference": "66b0bb4abda229bc073eff6bbc8f2685bdaac165",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/intl": "~2.3|~3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's ICU-related data and classes",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "icu",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2016-01-20 09:13:37"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "1289d16209491b584839022f29257ad859b8532d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d",
+                "reference": "1289d16209491b584839022f29257ad859b8532d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2016-01-20 09:13:37"
+        },
+        {
+            "name": "symfony/polyfill-php54",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php54.git",
+                "reference": "74663d5a2ff3c530c1bc0571500e0feec9094054"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/74663d5a2ff3c530c1bc0571500e0feec9094054",
+                "reference": "74663d5a2ff3c530c1bc0571500e0feec9094054",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php54\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2016-01-20 09:13:37"
+        },
+        {
+            "name": "symfony/polyfill-php55",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php55.git",
+                "reference": "b4f3f07d91702f8f926339fc4fcf81671d8c27e6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b4f3f07d91702f8f926339fc4fcf81671d8c27e6",
+                "reference": "b4f3f07d91702f8f926339fc4fcf81671d8c27e6",
+                "shasum": ""
+            },
+            "require": {
+                "ircmaxell/password-compat": "~1.0",
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php55\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2016-01-20 09:13:37"
+        },
+        {
+            "name": "symfony/polyfill-php56",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php56.git",
+                "reference": "4d891fff050101a53a4caabb03277284942d1ad9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/4d891fff050101a53a4caabb03277284942d1ad9",
+                "reference": "4d891fff050101a53a4caabb03277284942d1ad9",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/polyfill-util": "~1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php56\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2016-01-20 09:13:37"
+        },
+        {
+            "name": "symfony/polyfill-php70",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php70.git",
+                "reference": "8428ceddbbaf102f2906769a8ef2438220c5cb95"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/8428ceddbbaf102f2906769a8ef2438220c5cb95",
+                "reference": "8428ceddbbaf102f2906769a8ef2438220c5cb95",
+                "shasum": ""
+            },
+            "require": {
+                "paragonie/random_compat": "~1.0",
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php70\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2016-01-25 08:44:42"
+        },
+        {
+            "name": "symfony/polyfill-util",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-util.git",
+                "reference": "8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4",
+                "reference": "8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Util\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony utilities for portability of PHP codes",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compat",
+                "compatibility",
+                "polyfill",
+                "shim"
+            ],
+            "time": "2016-01-20 09:13:37"
+        },
+        {
+            "name": "symfony/security-acl",
+            "version": "v2.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/security-acl.git",
+                "reference": "4a3f7327ad215242c78f6564ad4ea6d2db1b8347"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/security-acl/zipball/4a3f7327ad215242c78f6564ad4ea6d2db1b8347",
+                "reference": "4a3f7327ad215242c78f6564ad4ea6d2db1b8347",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.9",
+                "symfony/security-core": "~2.4|~3.0.0"
+            },
+            "require-dev": {
+                "doctrine/common": "~2.2",
+                "doctrine/dbal": "~2.2",
+                "psr/log": "~1.0",
+                "symfony/phpunit-bridge": "~2.7|~3.0.0"
+            },
+            "suggest": {
+                "doctrine/dbal": "For using the built-in ACL implementation",
+                "symfony/class-loader": "For using the ACL generateSql script",
+                "symfony/finder": "For using the ACL generateSql script"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.8-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Security\\Acl\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Security Component - ACL (Access Control List)",
+            "homepage": "https://symfony.com",
+            "time": "2015-12-28 09:39:09"
+        },
+        {
+            "name": "symfony/swiftmailer-bundle",
+            "version": "v2.3.11",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/swiftmailer-bundle.git",
+                "reference": "5e1a90f28213231ceee19c953bbebc5b5b95c690"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/5e1a90f28213231ceee19c953bbebc5b5b95c690",
+                "reference": "5e1a90f28213231ceee19c953bbebc5b5b95c690",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2",
+                "swiftmailer/swiftmailer": ">=4.2.0,~5.0",
+                "symfony/config": "~2.3|~3.0",
+                "symfony/dependency-injection": "~2.3|~3.0",
+                "symfony/http-kernel": "~2.3|~3.0",
+                "symfony/yaml": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "~2.7|~3.0"
+            },
+            "suggest": {
+                "psr/log": "Allows logging"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Bundle\\SwiftmailerBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Symfony SwiftmailerBundle",
+            "homepage": "http://symfony.com",
+            "time": "2016-01-15 16:41:20"
+        },
+        {
+            "name": "symfony/symfony",
+            "version": "v2.8.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/symfony.git",
+                "reference": "7a9a5fce7ce6e448e527f635463dda00761e12c2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/symfony/zipball/7a9a5fce7ce6e448e527f635463dda00761e12c2",
+                "reference": "7a9a5fce7ce6e448e527f635463dda00761e12c2",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/common": "~2.4",
+                "php": ">=5.3.9",
+                "psr/log": "~1.0",
+                "symfony/polyfill-apcu": "~1.1",
+                "symfony/polyfill-intl-icu": "~1.0",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php54": "~1.0",
+                "symfony/polyfill-php55": "~1.0",
+                "symfony/polyfill-php56": "~1.0",
+                "symfony/polyfill-php70": "~1.0",
+                "symfony/polyfill-util": "~1.0",
+                "symfony/security-acl": "~2.7",
+                "twig/twig": "~1.23|~2.0"
+            },
+            "conflict": {
+                "phpdocumentor/reflection": "<1.0.7"
+            },
+            "replace": {
+                "symfony/asset": "self.version",
+                "symfony/browser-kit": "self.version",
+                "symfony/class-loader": "self.version",
+                "symfony/config": "self.version",
+                "symfony/console": "self.version",
+                "symfony/css-selector": "self.version",
+                "symfony/debug": "self.version",
+                "symfony/debug-bundle": "self.version",
+                "symfony/dependency-injection": "self.version",
+                "symfony/doctrine-bridge": "self.version",
+                "symfony/dom-crawler": "self.version",
+                "symfony/event-dispatcher": "self.version",
+                "symfony/expression-language": "self.version",
+                "symfony/filesystem": "self.version",
+                "symfony/finder": "self.version",
+                "symfony/form": "self.version",
+                "symfony/framework-bundle": "self.version",
+                "symfony/http-foundation": "self.version",
+                "symfony/http-kernel": "self.version",
+                "symfony/intl": "self.version",
+                "symfony/ldap": "self.version",
+                "symfony/locale": "self.version",
+                "symfony/monolog-bridge": "self.version",
+                "symfony/options-resolver": "self.version",
+                "symfony/process": "self.version",
+                "symfony/property-access": "self.version",
+                "symfony/property-info": "self.version",
+                "symfony/proxy-manager-bridge": "self.version",
+                "symfony/routing": "self.version",
+                "symfony/security": "self.version",
+                "symfony/security-bundle": "self.version",
+                "symfony/security-core": "self.version",
+                "symfony/security-csrf": "self.version",
+                "symfony/security-guard": "self.version",
+                "symfony/security-http": "self.version",
+                "symfony/serializer": "self.version",
+                "symfony/stopwatch": "self.version",
+                "symfony/swiftmailer-bridge": "self.version",
+                "symfony/templating": "self.version",
+                "symfony/translation": "self.version",
+                "symfony/twig-bridge": "self.version",
+                "symfony/twig-bundle": "self.version",
+                "symfony/validator": "self.version",
+                "symfony/var-dumper": "self.version",
+                "symfony/web-profiler-bundle": "self.version",
+                "symfony/yaml": "self.version"
+            },
+            "require-dev": {
+                "doctrine/data-fixtures": "1.0.*",
+                "doctrine/dbal": "~2.4",
+                "doctrine/doctrine-bundle": "~1.2",
+                "doctrine/orm": "~2.4,>=2.4.5",
+                "egulias/email-validator": "~1.2",
+                "monolog/monolog": "~1.11",
+                "ocramius/proxy-manager": "~0.4|~1.0|~2.0",
+                "phpdocumentor/reflection": "^1.0.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.8-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/",
+                    "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/",
+                    "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/",
+                    "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/",
+                    "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/",
+                    "Symfony\\Bundle\\": "src/Symfony/Bundle/",
+                    "Symfony\\Component\\": "src/Symfony/Component/"
+                },
+                "classmap": [
+                    "src/Symfony/Component/Intl/Resources/stubs"
+                ],
+                "exclude-from-classmap": [
+                    "**/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "The Symfony PHP framework",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "framework"
+            ],
+            "time": "2016-02-28 21:06:29"
+        },
+        {
+            "name": "twig/extensions",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/twigphp/Twig-extensions.git",
+                "reference": "449e3c8a9ffad7c2479c7864557275a32b037499"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/449e3c8a9ffad7c2479c7864557275a32b037499",
+                "reference": "449e3c8a9ffad7c2479c7864557275a32b037499",
+                "shasum": ""
+            },
+            "require": {
+                "twig/twig": "~1.20|~2.0"
+            },
+            "require-dev": {
+                "symfony/translation": "~2.3"
+            },
+            "suggest": {
+                "symfony/translation": "Allow the time_diff output to be translated"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Twig_Extensions_": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Common additional features for Twig that do not directly belong in core",
+            "homepage": "http://twig.sensiolabs.org/doc/extensions/index.html",
+            "keywords": [
+                "i18n",
+                "text"
+            ],
+            "time": "2015-08-22 16:38:35"
+        },
+        {
+            "name": "twig/twig",
+            "version": "v1.24.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/twigphp/Twig.git",
+                "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/twigphp/Twig/zipball/3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8",
+                "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2.7"
+            },
+            "require-dev": {
+                "symfony/debug": "~2.7",
+                "symfony/phpunit-bridge": "~2.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.24-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Twig_": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com",
+                    "homepage": "http://fabien.potencier.org",
+                    "role": "Lead Developer"
+                },
+                {
+                    "name": "Armin Ronacher",
+                    "email": "armin.ronacher@active-4.com",
+                    "role": "Project Founder"
+                },
+                {
+                    "name": "Twig Team",
+                    "homepage": "http://twig.sensiolabs.org/contributors",
+                    "role": "Contributors"
+                }
+            ],
+            "description": "Twig, the flexible, fast, and secure template language for PHP",
+            "homepage": "http://twig.sensiolabs.org",
+            "keywords": [
+                "templating"
+            ],
+            "time": "2016-01-25 21:22:18"
+        },
+        {
+            "name": "white-october/pagerfanta-bundle",
+            "version": "v1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/whiteoctober/WhiteOctoberPagerfantaBundle.git",
+                "reference": "e6b020f9c2af6fa5a46d780039c57fdb89003f6c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/whiteoctober/WhiteOctoberPagerfantaBundle/zipball/e6b020f9c2af6fa5a46d780039c57fdb89003f6c",
+                "reference": "e6b020f9c2af6fa5a46d780039c57fdb89003f6c",
+                "shasum": ""
+            },
+            "require": {
+                "pagerfanta/pagerfanta": "1.0.*",
+                "symfony/framework-bundle": "~2.3|~3.0",
+                "symfony/property-access": "~2.3|~3.0",
+                "symfony/twig-bundle": "~2.3|~3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~3.7",
+                "symfony/symfony": "~2.3|~3.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "WhiteOctober\\PagerfantaBundle\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Pablo Díez",
+                    "email": "pablodip@gmail.com"
+                }
+            ],
+            "description": "Bundle to use Pagerfanta with Symfony2",
+            "keywords": [
+                "page",
+                "paging"
+            ],
+            "time": "2015-12-06 17:16:06"
+        },
+        {
+            "name": "zendframework/zend-escaper",
+            "version": "2.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-escaper.git",
+                "reference": "a4b227d8a477f4e7e9073f8e0a7ae7dbd3104a73"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/a4b227d8a477f4e7e9073f8e0a7ae7dbd3104a73",
+                "reference": "a4b227d8a477f4e7e9073f8e0a7ae7dbd3104a73",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.23"
+            },
+            "require-dev": {
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.5-dev",
+                    "dev-develop": "2.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\Escaper\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "homepage": "https://github.com/zendframework/zend-escaper",
+            "keywords": [
+                "escaper",
+                "zf2"
+            ],
+            "time": "2015-06-03 14:05:37"
+        },
+        {
+            "name": "zendframework/zend-feed",
+            "version": "2.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-feed.git",
+                "reference": "12b328d382aa5200f1de53d4147033b885776b67"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/12b328d382aa5200f1de53d4147033b885776b67",
+                "reference": "12b328d382aa5200f1de53d4147033b885776b67",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0",
+                "zendframework/zend-escaper": "^2.5",
+                "zendframework/zend-stdlib": "^2.7 || ^3.0"
+            },
+            "require-dev": {
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "~4.0",
+                "psr/http-message": "^1.0",
+                "zendframework/zend-cache": "^2.5",
+                "zendframework/zend-db": "^2.5",
+                "zendframework/zend-http": "^2.5",
+                "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3",
+                "zendframework/zend-validator": "^2.5"
+            },
+            "suggest": {
+                "psr/http-message": "PSR-7 ^1.0, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator",
+                "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests",
+                "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub",
+                "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader",
+                "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations",
+                "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries ehen using the Writer subcomponent"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7-dev",
+                    "dev-develop": "2.8-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\Feed\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "provides functionality for consuming RSS and Atom feeds",
+            "homepage": "https://github.com/zendframework/zend-feed",
+            "keywords": [
+                "feed",
+                "zf2"
+            ],
+            "time": "2016-02-11 18:54:29"
+        },
+        {
+            "name": "zendframework/zend-json",
+            "version": "2.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-json.git",
+                "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28",
+                "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0"
+            },
+            "require-dev": {
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "~4.0",
+                "zendframework/zend-http": "^2.5.4",
+                "zendframework/zend-server": "^2.6.1",
+                "zendframework/zend-stdlib": "^2.5 || ^3.0",
+                "zendframework/zendxml": "^1.0.2"
+            },
+            "suggest": {
+                "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server",
+                "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server",
+                "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses",
+                "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6-dev",
+                    "dev-develop": "2.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\Json\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP",
+            "homepage": "https://github.com/zendframework/zend-json",
+            "keywords": [
+                "json",
+                "zf2"
+            ],
+            "time": "2016-02-04 21:20:26"
+        },
+        {
+            "name": "zendframework/zend-servicemanager",
+            "version": "2.7.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-servicemanager.git",
+                "reference": "fb5b54db5ead533b38e311f14e9c01a79218bf2b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/fb5b54db5ead533b38e311f14e9c01a79218bf2b",
+                "reference": "fb5b54db5ead533b38e311f14e9c01a79218bf2b",
+                "shasum": ""
+            },
+            "require": {
+                "container-interop/container-interop": "~1.0",
+                "php": "^5.5 || ^7.0"
+            },
+            "require-dev": {
+                "athletic/athletic": "dev-master",
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "~4.0",
+                "zendframework/zend-di": "~2.5",
+                "zendframework/zend-mvc": "~2.5"
+            },
+            "suggest": {
+                "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services",
+                "zendframework/zend-di": "Zend\\Di component"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7-dev",
+                    "dev-develop": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\ServiceManager\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "homepage": "https://github.com/zendframework/zend-servicemanager",
+            "keywords": [
+                "servicemanager",
+                "zf2"
+            ],
+            "time": "2016-02-02 14:11:46"
+        },
+        {
+            "name": "zendframework/zend-stdlib",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-stdlib.git",
+                "reference": "22eb098958980fbbe6b9a06f209f5a4b496cc0c1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/22eb098958980fbbe6b9a06f209f5a4b496cc0c1",
+                "reference": "22eb098958980fbbe6b9a06f209f5a4b496cc0c1",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0"
+            },
+            "require-dev": {
+                "athletic/athletic": "~0.1",
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev",
+                    "dev-develop": "3.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\Stdlib\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "homepage": "https://github.com/zendframework/zend-stdlib",
+            "keywords": [
+                "stdlib",
+                "zf2"
+            ],
+            "time": "2016-02-03 16:53:37"
+        },
+        {
+            "name": "zendframework/zend-uri",
+            "version": "2.5.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-uri.git",
+                "reference": "0bf717a239432b1a1675ae314f7c4acd742749ed"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/0bf717a239432b1a1675ae314f7c4acd742749ed",
+                "reference": "0bf717a239432b1a1675ae314f7c4acd742749ed",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0",
+                "zendframework/zend-escaper": "^2.5",
+                "zendframework/zend-validator": "^2.5"
+            },
+            "require-dev": {
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.5-dev",
+                    "dev-develop": "2.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\Uri\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "a component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)",
+            "homepage": "https://github.com/zendframework/zend-uri",
+            "keywords": [
+                "uri",
+                "zf2"
+            ],
+            "time": "2016-02-17 22:38:51"
+        },
+        {
+            "name": "zendframework/zend-validator",
+            "version": "2.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-validator.git",
+                "reference": "1315fead53358054e3f5fcf440c1a4cd5f0724db"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/1315fead53358054e3f5fcf440c1a4cd5f0724db",
+                "reference": "1315fead53358054e3f5fcf440c1a4cd5f0724db",
+                "shasum": ""
+            },
+            "require": {
+                "container-interop/container-interop": "^1.1",
+                "php": "^5.5 || ^7.0",
+                "zendframework/zend-stdlib": "^2.7 || ^3.0"
+            },
+            "require-dev": {
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "^4.0",
+                "zendframework/zend-cache": "^2.6.1",
+                "zendframework/zend-config": "^2.6",
+                "zendframework/zend-db": "^2.5",
+                "zendframework/zend-filter": "^2.6",
+                "zendframework/zend-http": "^2.5.4",
+                "zendframework/zend-i18n": "^2.6",
+                "zendframework/zend-math": "^2.6",
+                "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3",
+                "zendframework/zend-session": "^2.5",
+                "zendframework/zend-uri": "^2.5"
+            },
+            "suggest": {
+                "zendframework/zend-db": "Zend\\Db component",
+                "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator",
+                "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages as well as to use the various Date validators",
+                "zendframework/zend-i18n-resources": "Translations of validator messages",
+                "zendframework/zend-math": "Zend\\Math component",
+                "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains",
+                "zendframework/zend-session": "Zend\\Session component",
+                "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6-dev",
+                    "dev-develop": "2.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\Validator\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "provides a set of commonly needed validators",
+            "homepage": "https://github.com/zendframework/zend-validator",
+            "keywords": [
+                "validator",
+                "zf2"
+            ],
+            "time": "2016-02-17 17:59:34"
+        },
+        {
+            "name": "zendframework/zend-version",
+            "version": "2.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zendframework/zend-version.git",
+                "reference": "e30c55dc394eaf396f0347887af0a7bef471fe08"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zendframework/zend-version/zipball/e30c55dc394eaf396f0347887af0a7bef471fe08",
+                "reference": "e30c55dc394eaf396f0347887af0a7bef471fe08",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.23",
+                "zendframework/zend-json": "~2.5"
+            },
+            "require-dev": {
+                "fabpot/php-cs-fixer": "1.7.*",
+                "phpunit/phpunit": "~4.0",
+                "zendframework/zend-http": "~2.5"
+            },
+            "suggest": {
+                "zendframework/zend-http": "Allows use of Zend\\Http\\Client to check version information",
+                "zendframework/zend-json": "To check latest version hosted in GitHub"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.5-dev",
+                    "dev-develop": "2.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Zend\\Version\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "homepage": "https://github.com/zendframework/zend-version",
+            "keywords": [
+                "version",
+                "zf2"
+            ],
+            "time": "2015-06-04 15:41:05"
+        }
+    ],
+    "packages-dev": [
+        {
+            "name": "symfony/phpunit-bridge",
+            "version": "v3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/phpunit-bridge.git",
+                "reference": "4580ae86cde5497d38fc971192cd2c37e546eb4f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/4580ae86cde5497d38fc971192cd2c37e546eb4f",
+                "reference": "4580ae86cde5497d38fc971192cd2c37e546eb4f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.9"
+            },
+            "suggest": {
+                "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
+            },
+            "type": "symfony-bridge",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Bridge\\PhpUnit\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony PHPUnit Bridge",
+            "homepage": "https://symfony.com",
+            "time": "2016-01-21 09:38:31"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": {
+        "composer/composer": 20,
+        "friendsofsymfony/user-bundle": 20,
+        "drupal/parse-composer": 20
+    },
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": ">=5.6"
+    },
+    "platform-dev": []
+}
diff --git a/doc/schema.xml b/doc/schema.xml
new file mode 100644
index 0000000..24e8f10
--- /dev/null
+++ b/doc/schema.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<schema name="packagist" version="1.4">
+  <types>
+    <!-- The StrField type is not analyzed, but indexed/stored verbatim. -->
+    <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
+    <!-- boolean type: "true" or "false" -->
+    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
+    <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
+    <fieldtype name="binary" class="solr.BinaryField"/>
+
+    <!-- The optional sortMissingLast and sortMissingFirst attributes are
+         currently supported on types that are sorted internally as strings
+         and on numeric types.
+	       This includes "string","boolean", and, as of 3.5 (and 4.x),
+	       int, float, long, date, double, including the "Trie" variants.
+       - If sortMissingLast="true", then a sort on this field will cause documents
+         without the field to come after documents with the field,
+         regardless of the requested sort order (asc or desc).
+       - If sortMissingFirst="true", then a sort on this field will cause documents
+         without the field to come before documents with the field,
+         regardless of the requested sort order.
+       - If sortMissingLast="false" and sortMissingFirst="false" (the default),
+         then default lucene sorting will be used which places docs without the
+         field first in an ascending sort and last in a descending sort.
+    -->
+
+    <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+
+    <fieldType name="date" class="solr.TrieDateField" omitNorms="true" precisionStep="0" positionIncrementGap="0"/>
+
+    <!-- A Trie based date field for faster date range queries and date faceting. -->
+    <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>
+
+    <!-- A edge-ngram'd text field that can be used for wildcard matching -->
+    <fieldType name="text_edgengram" class="solr.TextField" positionIncrementGap="100">
+        <analyzer type="index">
+            <tokenizer class="solr.StandardTokenizerFactory"/>
+            <filter class="solr.WordDelimiterFilterFactory" splitOnCaseChange="1" generateWordParts="1" preserveOriginal="1"/>
+            <filter class="solr.LowerCaseFilterFactory"/>
+            <filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="100" />
+        </analyzer>
+        <analyzer type="query">
+            <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+            <filter class="solr.LowerCaseFilterFactory"/>
+        </analyzer>
+    </fieldType>
+
+    <!-- A general text field that has reasonable, generic
+         cross-language defaults: it tokenizes with StandardTokenizer,
+	 removes stop words from case-insensitive "stopwords.txt"
+	 (empty by default), and down cases.  At query time only, it
+	 also applies synonyms. -->
+    <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Just like text_general except it reverses the characters of
+	 each token, to enable more efficient leading wildcard queries. -->
+    <fieldType name="text_general_rev" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.ReversedWildcardFilterFactory" withOriginal="true"
+           maxPosAsterisk="3" maxPosQuestion="2" maxFractionAsterisk="0.33"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- A text field with defaults appropriate for English: it
+         tokenizes with StandardTokenizer, removes English stop words
+         (lang/stopwords_en.txt), down cases, protects words from protwords.txt, and
+         finally applies Porter's stemming.  The query time analyzer
+         also applies synonyms from synonyms.txt. -->
+    <fieldType name="text_en" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- in this example, we will only use synonyms at query time
+        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
+        -->
+        <!-- Case insensitive stop word removal.
+          add enablePositionIncrements=true in both the index and query
+          analyzers to leave a 'gap' for more accurate phrase queries.
+        -->
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.EnglishPossessiveFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+	<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+        <filter class="solr.EnglishMinimalStemFilterFactory"/>
+	-->
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.EnglishPossessiveFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+	<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+        <filter class="solr.EnglishMinimalStemFilterFactory"/>
+	-->
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- A text field with defaults appropriate for English, plus
+	 aggressive word-splitting and autophrase features enabled.
+	 This field is just like text_en, except it adds
+	 WordDelimiterFilter to enable splitting and matching of
+	 words on case-change, alpha numeric boundaries, and
+	 non-alphanumeric chars.  This means certain compound word
+	 cases will work, for example query "wi fi" will match
+	 document "WiFi" or "wi-fi".  However, other cases will still
+	 not match, for example if the query is "wifi" and the
+	 document is "wi fi" or if the query is "wi-fi" and the
+	 document is "wifi".
+        -->
+    <fieldType name="text_en_splitting" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
+      <analyzer type="index">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <!-- in this example, we will only use synonyms at query time
+        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
+        -->
+        <!-- Case insensitive stop word removal.
+          add enablePositionIncrements=true in both the index and query
+          analyzers to leave a 'gap' for more accurate phrase queries.
+        -->
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Less flexible matching, but less false matches.  Probably not ideal for product names,
+         but may be good for SKUs.  Can insert dashes in the wrong place and still match. -->
+    <fieldType name="text_en_splitting_tight" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
+      <analyzer>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_en.txt"/>
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+        <filter class="solr.EnglishMinimalStemFilterFactory"/>
+        <!-- this filter can remove any duplicate tokens that appear at the same position - sometimes
+             possible with WordDelimiterFilter in conjuncton with stemming. -->
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <fieldtype name="phonetic" stored="false" indexed="true" class="solr.TextField" >
+      <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.DoubleMetaphoneFilterFactory" inject="false"/>
+      </analyzer>
+    </fieldtype>
+
+    <!-- since fields of this type are by default not stored or indexed,
+         any data added to them will be ignored outright.  -->
+    <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />
+ </types>
+
+ <fields>
+   <!-- Valid attributes for fields:
+     name: mandatory - the name for the field
+     type: mandatory - the name of a previously defined type from the
+       <types> section
+     indexed: true if this field should be indexed (searchable or sortable)
+     stored: true if this field should be retrievable
+     multiValued: true if this field may contain multiple values per document
+     omitNorms: (expert) set to true to omit the norms associated with
+       this field (this disables length normalization and index-time
+       boosting for the field, and saves some memory).  Only full-text
+       fields or fields that need an index-time boost need norms.
+     termVectors: [false] set to true to store the term vector for a
+       given field.
+       When using MoreLikeThis, fields used for similarity should be
+       stored for best performance.
+     termPositions: Store position information with the term vector.
+       This will increase storage costs.
+     termOffsets: Store offset information with the term vector. This
+       will increase storage costs.
+     default: a value that should be used if no value is specified
+       when adding a document.
+   -->
+
+   <field name="id" type="string" indexed="true" stored="true" required="true" />
+   <field name="name" type="text_general_rev" indexed="true" stored="true"/>
+   <field name="description" type="text_general_rev" indexed="true" stored="true"/>
+   <field name="tags" type="text_general_rev" indexed="true" stored="true" multiValued="true"/>
+   <field name="type" type="text_general_rev" indexed="true" stored="true"/>
+   <field name="trendiness" type="float" indexed="true" stored="true" />
+   <field name="downloads" type="int" indexed="true" stored="true" />
+   <field name="favers" type="int" indexed="true" stored="true" />
+   <field name="language" type="string" indexed="true" stored="true" />
+   <field name="repository" type="string" indexed="false" stored="true" />
+   <field name="abandoned" type="int" indexed="false" stored="true" />
+   <field name="replacementPackage" type="string" indexed="false" stored="true" />
+
+   <!-- catchall field, containing all other searchable text fields (implemented
+        via copyField further on in this schema  -->
+   <field name="text" type="text_en" indexed="true" stored="false" multiValued="true"/>
+
+   <!-- extra name field allowing dashes to be omitted/misplaced -->
+   <field name="name_split" type="text_en_splitting_tight" indexed="true" stored="false" />
+
+   <!-- extra catchall for ngram searches -->
+   <field name="text_ngram" type="text_edgengram" indexed="true" stored="false" multiValued="true" />
+ </fields>
+
+ <!-- Field to use to determine and enforce document uniqueness.
+      Unless this field is marked with required="false", it will be a required field
+   -->
+ <uniqueKey>id</uniqueKey>
+
+ <!-- field for the QueryParser to use when an explicit fieldname is absent -->
+ <defaultSearchField>text</defaultSearchField>
+
+ <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
+ <solrQueryParser defaultOperator="OR"/>
+  <!-- copyField commands copy one field to another at the time a document
+        is added to the index.  It's used either to index the same field differently,
+        or to add multiple fields to the same field for easier/faster searching.  -->
+
+   <copyField source="name" dest="text"/>
+   <copyField source="description" dest="text"/>
+   <copyField source="tags" dest="text"/>
+
+   <copyField source="name" dest="name_split"/>
+
+   <copyField source="name" dest="text_ngram"/>
+   <copyField source="tags" dest="text_ngram"/>
+</schema>
diff --git a/doc/solrconfig.xml b/doc/solrconfig.xml
new file mode 100644
index 0000000..7a5b2f9
--- /dev/null
+++ b/doc/solrconfig.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<!--
+ This is a stripped down config file used for a simple example...
+ It is *not* a good example to work from.
+-->
+<config>
+  <luceneMatchVersion>LUCENE_36</luceneMatchVersion>
+  <!--  The DirectoryFactory to use for indexes.
+        solr.StandardDirectoryFactory, the default, is filesystem based.
+        solr.RAMDirectoryFactory is memory based, not persistent, and doesn't work with replication. -->
+  <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.StandardDirectoryFactory}"/>
+
+
+  <updateHandler class="solr.DirectUpdateHandler2" />
+
+  <requestDispatcher handleSelect="true" >
+    <requestParsers enableRemoteStreaming="false" multipartUploadLimitInKB="2048" />
+  </requestDispatcher>
+
+  <requestHandler name="standard" class="solr.StandardRequestHandler" default="true" />
+  <requestHandler name="/update" class="solr.XmlUpdateRequestHandler" />
+  <requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" />
+
+  <!-- config for the admin interface -->
+  <admin>
+    <defaultQuery>solr</defaultQuery>
+  </admin>
+
+</config>
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..6855231
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
+<phpunit
+    backupGlobals               = "false"
+    backupStaticAttributes      = "false"
+    colors                      = "true"
+    convertErrorsToExceptions   = "true"
+    convertNoticesToExceptions  = "true"
+    convertWarningsToExceptions = "true"
+    processIsolation            = "false"
+    stopOnFailure               = "false"
+    syntaxCheck                 = "false"
+    bootstrap                   = "app/autoload.php" >
+
+    <testsuites>
+        <testsuite name="Packagist Test Suite">
+            <directory>src/*/*Bundle/Tests</directory>
+            <directory>src/*/Bundle/*Bundle/Tests</directory>
+        </testsuite>
+    </testsuites>
+
+    <php>
+        <server name="KERNEL_DIR" value="app/" />
+    </php>
+
+    <filter>
+        <whitelist>
+            <directory>src</directory>
+            <exclude>
+                <directory>src/*/*Bundle/Resources</directory>
+                <directory>src/*/*Bundle/Tests</directory>
+                <directory>src/*/Bundle/*Bundle/Resources</directory>
+                <directory>src/*/Bundle/*Bundle/Tests</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/src/.htaccess b/src/.htaccess
new file mode 100644
index 0000000..3418e55
--- /dev/null
+++ b/src/.htaccess
@@ -0,0 +1 @@
+deny from all
\ No newline at end of file
diff --git a/src/DrupalPackagist/Bundle/Command/DrupalOgCommitLogParserCommand.php b/src/DrupalPackagist/Bundle/Command/DrupalOgCommitLogParserCommand.php
new file mode 100644
index 0000000..9d4202b
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Command/DrupalOgCommitLogParserCommand.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Contains \Packagist\WebBundle\Command\DrupalOgCommitLogParserCommand.
+ */
+
+namespace DrupalPackagist\Bundle\Command;
+
+use Doctrine\ORM\NoResultException;
+use Guzzle\Service\Client;
+use Packagist\WebBundle\Package\Updater;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\DomCrawler\Crawler;
+
+class DrupalOgCommitLogParserCommand extends ContainerAwareCommand
+{
+    const VENDOR = 'drupal';
+
+    protected function configure()
+    {
+        $this->setName('packagist:drupal_org_parse_commitlog')
+            ->setDescription('Updates packages with Drupal.org commit log information');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $client = new Client();
+        $request = $client->get('https://www.drupal.org/commitlog?' . time());
+        $response = $request->send();
+
+        $crawler = new Crawler((string) $response->getBody());
+        $packages = array();
+
+        $crawler->filter('.commit-global')->each(function (Crawler $node, $i) use (&$packages) {
+            $url = $node->filter('a')->extract(array('href'))[0];
+            $commit = $url . ' ' . $node->filter(".commit-info a")->text();
+            if (strpos($url, '/project/') === 0) {
+                $packages[$commit] = substr($url, strlen('/project/'));
+            }
+        });
+
+        $packages = array_reverse($packages);
+
+        /**
+         * @var $redis \Predis\Client
+         */
+        $redis = $this->getContainer()->get('snc_redis.default');
+        $commitlog = $redis->lrange('commitlog', 0, 199);
+
+        $diff = array_diff(array_keys($packages), $commitlog);
+
+        if (empty($diff)) {
+          return;
+        }
+
+        $redis->lpush('commitlog', $diff);
+        $redis->ltrim('commitlog', 0, 199);
+
+        $diff = array_values(array_unique(array_filter($diff)));
+
+        $process = array();
+        foreach ($diff as $key) {
+          $process[] = $packages[$key];
+        }
+
+        $packages = array_values(array_unique(array_filter($process)));
+        $tasks = array();
+
+        /** @var $packageRepo \Packagist\WebBundle\Entity\PackageRepository */
+        $packageRepo = $this->getContainer()->get('doctrine')->getRepository('PackagistWebBundle:Package');
+
+        /** @var $package\Packagist\WebBundle\Entity\Package */
+        foreach ($packages as $name) {
+            try {
+                $packageRepo->findOneByName(self::VENDOR . '/' . $name);
+                $tasks['update'][] = $name;
+            }
+            catch (NoResultException $e) {
+                $tasks['add'][] = $name;
+            }
+        }
+
+        if (isset($tasks['add'])) {
+            $client = $this->getContainer()->get('old_sound_rabbit_mq.add_packages_producer');
+            foreach ($tasks['add'] as $name) {
+                $output->write('Queuing add job ' . self::VENDOR . '/' . $name, TRUE);
+                $client->publish(
+                  serialize(
+                    array(
+                      'package_name' => self::VENDOR . '/' . $name,
+                      'url' => 'https://git.drupal.org/project/' . $name  . '.git'
+                    )
+                  )
+                );
+            }
+        }
+
+        if (isset($tasks['update'])) {
+            $client = $this->getContainer()->get('old_sound_rabbit_mq.update_packages_producer');
+            $input->setInteractive(FALSE);
+            foreach ($tasks['update'] as $name) {
+                $name = self::VENDOR . '/' . $name;
+                $output->write('Queuing update job ' . $name, TRUE);
+                $client->publish(
+                  serialize(
+                    array(
+                      'flags' => Updater::UPDATE_EQUAL_REFS,
+                      'package_name' => $name
+                    )
+                  )
+                );
+            }
+        }
+
+    }
+}
diff --git a/src/DrupalPackagist/Bundle/Command/DrupalOgProjectReleaseApiParserCommand.php b/src/DrupalPackagist/Bundle/Command/DrupalOgProjectReleaseApiParserCommand.php
new file mode 100644
index 0000000..6cbd435
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Command/DrupalOgProjectReleaseApiParserCommand.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Contains \Packagist\WebBundle\Command\DrupalOgProjectReleaseApiParserCommand.
+ */
+
+namespace DrupalPackagist\Bundle\Command;
+
+use Doctrine\ORM\NoResultException;
+use Guzzle\Service\Client;
+use Packagist\WebBundle\Package\Updater;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\DomCrawler\Crawler;
+
+class DrupalOgProjectReleaseApiParserCommand extends ContainerAwareCommand
+{
+    const VENDOR = 'drupal';
+
+    protected $maxRecords = 100;
+
+    protected $redisKey = 'drupal_org_parse_project_release';
+
+    protected function configure()
+    {
+        $this->setName('packagist:drupal_org_parse_project_release')
+            ->setDescription('Updates packages with Drupal.org project_release information');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $client = new Client();
+        $request = $client->get('https://www.drupal.org/api-d7/node.json?type=project_release&field_release_build_type=static&sort=created&direction=DESC');
+        $response = $request->send();
+        $releases = $response->json();
+
+        $packages = array();
+        foreach ($releases['list'] as $release) {
+            list($name, $version) = explode(' ', $release['title']);
+            $packages[$release['title']] = $name;
+        }
+
+        $packages = array_reverse($packages);
+
+        /**
+         * @var $redis \Predis\Client
+         */
+        $redis = $this->getContainer()->get('snc_redis.default');
+        $queue = $redis->lrange($this->redisKey, 0, $this->maxRecords);
+
+        $diff = array_diff(array_keys($packages), $queue);
+
+        if (empty($diff)) {
+          return;
+        }
+
+        $redis->lpush($this->redisKey, $diff);
+        $redis->ltrim($this->redisKey, 0, $this->maxRecords);
+
+        $diff = array_values(array_unique(array_filter($diff)));
+
+        $process = array();
+        foreach ($diff as $key) {
+          $process[] = $packages[$key];
+        }
+
+        $packages = array_values(array_unique(array_filter($process)));
+        $tasks = array();
+
+        /** @var $packageRepo \Packagist\WebBundle\Entity\PackageRepository */
+        $packageRepo = $this->getContainer()->get('doctrine')->getRepository('PackagistWebBundle:Package');
+
+        /** @var $package\Packagist\WebBundle\Entity\Package */
+        foreach ($packages as $name) {
+            try {
+                $packageRepo->findOneByName(self::VENDOR . '/' . $name);
+                $tasks['update'][] = $name;
+            }
+            catch (NoResultException $e) {
+                $tasks['add'][] = $name;
+            }
+        }
+
+        if (isset($tasks['add'])) {
+            $client = $this->getContainer()->get('old_sound_rabbit_mq.add_packages_producer');
+            foreach ($tasks['add'] as $name) {
+                $output->write('Queuing add job ' . self::VENDOR . '/' . $name, TRUE);
+                $client->publish(
+                  serialize(
+                    array(
+                      'package_name' => self::VENDOR . '/' . $name,
+                      'url' => 'https://git.drupal.org/project/' . $name  . '.git'
+                    )
+                  )
+                );
+            }
+        }
+
+        if (isset($tasks['update'])) {
+            $client = $this->getContainer()->get('old_sound_rabbit_mq.update_packages_producer');
+            $input->setInteractive(FALSE);
+            foreach ($tasks['update'] as $name) {
+                $name = self::VENDOR . '/' . $name;
+                $output->write('Queuing update job ' . $name, TRUE);
+                $client->publish(
+                  serialize(
+                    array(
+                      'flags' => Updater::UPDATE_EQUAL_REFS,
+                      'package_name' => $name
+                    )
+                  )
+                );
+            }
+        }
+
+    }
+}
diff --git a/src/DrupalPackagist/Bundle/Command/DrupalOrgModuleIndexParserCommand.php b/src/DrupalPackagist/Bundle/Command/DrupalOrgModuleIndexParserCommand.php
new file mode 100644
index 0000000..01e6cc2
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Command/DrupalOrgModuleIndexParserCommand.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @file
+ * Contains \Packagist\WebBundle\Command\DrupalOgCommitLogParserCommand.
+ */
+
+namespace DrupalPackagist\Bundle\Command;
+
+use Doctrine\ORM\NoResultException;
+use Guzzle\Http\Exception\ClientErrorResponseException;
+use Guzzle\Service\Client;
+use Packagist\WebBundle\Package\Updater;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\DomCrawler\Crawler;
+
+class DrupalOrgModuleIndexParserCommand extends ContainerAwareCommand
+{
+
+    const VENDOR = 'drupal';
+
+    protected function configure()
+    {
+        $this->setName('packagist:drupal_org_module_index_parser');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $packages = array();
+        $client = new Client();
+
+        $urls = array(
+          'https://www.drupal.org/project/project_distribution/index?project-status=full&drupal_core=103', // 7.x
+          'https://www.drupal.org/project/project_distribution/index?project-status=full&drupal_core=7234', // 8.x
+          'https://www.drupal.org/project/project_module/index?project-status=full&drupal_core=103', // 7.x
+          'https://www.drupal.org/project/project_module/index?project-status=full&drupal_core=7234', // 8.x
+          'https://www.drupal.org/project/project_theme/index?project-status=full&drupal_core=103', // 7.x
+          'https://www.drupal.org/project/project_theme/index?project-status=full&drupal_core=7234', // 8.x
+        );
+
+        foreach ($urls as $url) {
+            $request = $client->get($url);
+            $response = $request->send();
+
+            $crawler = new Crawler((string) $response->getBody());
+            $crawler->filter('.view-project-index .views-field-title a')
+              ->each(function (Crawler $node, $i) use (&$packages) {
+                  $name = $node->extract('href')[0];
+                  $packages[$name] = str_replace('/project/', '', $name);
+              });
+        }
+
+        $client = $this->getContainer()
+          ->get('old_sound_rabbit_mq.add_packages_producer');
+        foreach ($packages as $name) {
+            $output->write('Queuing add job ' . self::VENDOR . '/' . $name,
+              true);
+            $client->publish(
+              serialize(
+                array(
+                  'package_name' => self::VENDOR . '/' . $name,
+                  'url' => 'https://git.drupal.org/project/' . $name . '.git'
+                )
+              )
+            );
+        }
+    }
+
+}
diff --git a/src/DrupalPackagist/Bundle/Command/DrupalOrgUpdatePackagesCommand.php b/src/DrupalPackagist/Bundle/Command/DrupalOrgUpdatePackagesCommand.php
new file mode 100644
index 0000000..cf28b8e
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Command/DrupalOrgUpdatePackagesCommand.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace DrupalPackagist\Bundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Packagist\WebBundle\Entity\Package;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use FastFeed\Factory;
+use Symfony\Component\Console\Input\ArrayInput;
+
+class DrupalOrgUpdatePackagesCommand extends ContainerAwareCommand
+{
+    protected function configure()
+    {
+        $this->setName('packagist:drupal_org_update')
+            ->setDescription('Updates packages with Drupal.org rss information');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $parser = Factory::create();
+        $parser->addFeed(
+            'drupalOrg7x',
+            'https://www.drupal.org/taxonomy/term/103/feed'
+        );
+        foreach ($parser->fetch('drupalOrg7x') as $item) {
+            $update[current(explode(' ', $item->getName()))] = true;
+        }
+        $this->getApplication()->find('packagist:upsert')->run(
+            new ArrayInput([
+                'command' => 'packagist:upsert',
+                'packages' => array_keys($update),
+                '--repo-pattern' => 'https://git.drupal.org/project/%2$s',
+                '--vendor' => 'drupal'
+            ]),
+            $output
+        );
+    }
+}
diff --git a/src/DrupalPackagist/Bundle/Command/UpdateGitRepositoryUrlCommand.php b/src/DrupalPackagist/Bundle/Command/UpdateGitRepositoryUrlCommand.php
new file mode 100644
index 0000000..3aa4faa
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Command/UpdateGitRepositoryUrlCommand.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Contains \Packagist\WebBundle\Command\DrupalOgCommitLogParserCommand.
+ */
+
+namespace DrupalPackagist\Bundle\Command;
+
+use Guzzle\Http\Exception\ClientErrorResponseException;
+use Guzzle\Service\Client;
+use Packagist\WebBundle\Package\Updater;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\DomCrawler\Crawler;
+
+class UpdateGitRepositoryUrlCommand extends ContainerAwareCommand
+{
+    const VENDOR = 'drupal';
+
+    /**
+     * @var \Guzzle\Service\ClientInterface
+     */
+    protected $httpClient;
+
+    /**
+     * @var \OldSound\RabbitMqBundle\RabbitMq\Producer
+     */
+    protected $queue;
+
+    protected function configure()
+    {
+        $this->setName('packagist:drupal_org_update_repository_url')
+          ->setDefinition(array(
+            new InputArgument('package', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Package name to update')
+          ));
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        /**
+         * @var \Doctrine\ORM\EntityManager $em;
+         */
+        $em = $this->getContainer()->get('doctrine')->getEntityManager();
+
+        $this->httpClient = new Client();
+        $this->queue = $this->getContainer()->get('old_sound_rabbit_mq.update_packages_producer');
+
+        $packages = array_filter($input->getArgument('package'));
+        if (!empty($packages)) {
+          $names = $packages;
+          $packages = array();
+          $repository = $em->getRepository('Packagist\WebBundle\Entity\Package');
+          foreach ($names as $name) {
+            $packages[] = $repository->findOneBy(array('name' => $name));
+          }
+        }
+        else {
+          // $query = $em->createQuery("SELECT p FROM Packagist\WebBundle\Entity\Package p LEFT JOIN p.versions v WHERE p.type IS NULL AND v.id IS NULL ORDER BY p.updatedAt ASC");
+          $query = $em->createQuery("SELECT p FROM Packagist\WebBundle\Entity\Package p WHERE p.type IS NULL ORDER BY p.updatedAt ASC");
+          $packages = $query->getResult();
+        }
+
+        /**
+         * @var \Packagist\WebBundle\Entity\Package[] $packages
+         */
+        foreach ($packages as $package) {
+          $output->write('Crawl drupal.org/project/' . $package->getPackageName(), TRUE);
+          $content = NULL;
+
+          try {
+            $request = $this->httpClient->get('https://www.drupal.org/project/' . $package->getPackageName());
+            $response = $request->send();
+            $content = (string) $response->getBody();
+          }
+          catch (ClientErrorResponseException $e) {
+            $output->write($e->getMessage());
+            continue;
+          }
+
+          $crawler = new Crawler($content);
+          $result = $crawler->filter('#block-drupalorg-project-development a')
+            ->reduce(function (Crawler $node, $i) {
+              return $node->text() == 'Browse code repository';
+            })
+            ->attr('href');
+
+          if (!empty($result)) {
+            $package->setRepository(str_replace('http://drupalcode.org', 'https://git.drupal.org', $result));
+            $em->persist($package);
+            $em->flush();
+
+            $output->write('Queuing update job ' . $package->getName(), TRUE);
+            $this->queue->publish(
+              serialize(
+                array(
+                  'flags' => Updater::UPDATE_EQUAL_REFS,
+                  'package_name' => $package->getName()
+                )
+              )
+            );
+          }
+          else {
+            $output->write('Git Repo url for ' . $package->getName() . ' not found.');
+            $package->setCrawledAt(new \DateTime());
+            $em->persist($package);
+            $em->flush();
+          }
+        }
+    }
+}
diff --git a/src/DrupalPackagist/Bundle/DrupalPackagistBundle.php b/src/DrupalPackagist/Bundle/DrupalPackagistBundle.php
new file mode 100644
index 0000000..75e2f71
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/DrupalPackagistBundle.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace DrupalPackagist\Bundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class DrupalPackagistBundle extends Bundle
+{
+    public function getParent()
+    {
+        return 'PackagistWebBundle';
+    }
+}
diff --git a/src/DrupalPackagist/Bundle/Package/Updater.php b/src/DrupalPackagist/Bundle/Package/Updater.php
new file mode 100644
index 0000000..b3a72a4
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Package/Updater.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \DrupalPackagist\Bundle\Package\Updater.
+ */
+
+namespace DrupalPackagist\Bundle\Package;
+
+use Composer\Config;
+use Composer\IO\IOInterface;
+use Packagist\WebBundle\Package\Updater as PackagistUpdater;
+use Packagist\WebBundle\Entity\Package;
+use Composer\Repository\RepositoryInterface;
+use Drupal\ParseComposer\Repository;
+
+class Updater extends PackagistUpdater
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    public function update(
+      IOInterface $io,
+      Config $config,
+      Package $package,
+      RepositoryInterface $repository,
+      $flags = 0,
+      \DateTime $start = null
+    ) {
+        $repository = Repository::create($repository);
+        parent::update($io, $config, $package, $repository, $flags, $start);
+    }
+}
diff --git a/src/DrupalPackagist/Bundle/Resources/public/css/main.css b/src/DrupalPackagist/Bundle/Resources/public/css/main.css
new file mode 100644
index 0000000..82d609a
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/css/main.css
@@ -0,0 +1,1579 @@
+@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300,500,600,300italic);
+
+/*
+ main header: #2d2d32
+ secondary header: #0678be
+
+ light grey: #f3f4f5;
+ colors danger/error: #ff4533 #cd3729
+ colors warning: #ed9e2e #0678be
+ colors success: #83c129 #69ad21
+ colors default: #bbbfc4 #a5aab0
+
+ */
+
+@font-face {
+    font-family: 'fontello';
+    src: url('../font/fontello.eot');
+    src: url('../font/fontello.eot?#iefix') format('embedded-opentype'),
+    url('../font/fontello.woff') format('woff'),
+    url('../font/fontello.ttf') format('truetype'),
+    url('../font/fontello.svg#fontello') format('svg');
+    font-weight: normal;
+    font-style: normal;
+}
+
+html, body {
+    height: 100%;
+}
+
+pre, code {
+    background: #fff;
+    font-family: "Source Code Pro", Menlo, Consolas, "Courier New", Courier, monospace;
+    border-radius: 0;
+    border: none;
+    color: #2d2d32;
+    line-height: 12px;
+    font-size: 12px;
+}
+pre {
+  border-left: 2px solid #2d2d32;
+}
+
+label {
+  display: block;
+  font-weight: normal;
+}
+
+body {
+    background: #FAFAFA;
+    font-family: 'Open Sans', sans-serif;
+    padding-top: 58px;
+}
+.logo {
+  position: absolute;
+  width: 100px;
+  left: -98px;
+  top: -43px;
+}
+@media (min-width: 1200px) {
+  .logo {
+    width: 80px;
+    left: -90px;
+  }
+}
+
+hr {
+  border-color: #0678be;
+}
+a, .package .package-aside a {
+  color: #0077C0;
+}
+a:hover, .package .package-aside a:hover {
+  color: #2d2d32;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  font-family: 'Open Sans', sans-serif;
+  font-weight: 300;
+}
+i, cite, em, var, address, dfn {
+  font-style: italic;
+  font-weight: 300;
+}
+strong {
+  font-weight: 600;
+}
+.h3, h3 {
+    font-size: 16px;
+    font-weight: 600;
+}
+
+.navbar-wrapper {
+    left: 0;
+    right: 0;
+    top: 0;
+    min-height: 58px;
+    z-index: 15;
+    background: #2d2d32;
+}
+
+.navbar {
+    margin: 0;
+    border: 0;
+    -webkit-font-smoothing: subpixel-antialiased;
+}
+
+.navbar .navbar-brand {
+    padding: 12px 4px;
+    line-height: 32px;
+    margin: 0;
+}
+
+.navbar .navbar-brand a {
+    float: left;
+    font-size: 24px;
+    font-weight: 600;
+    color: #fff;
+    text-decoration: none;
+}
+
+.navbar .navbar-brand em {
+    padding-left: 15px;
+    padding-top: 8px;
+    font-size: 14px;
+    color: #f3f4f5;
+}
+
+.navbar-collapse {
+    border: 0;
+}
+
+.navbar-toggle {
+    background: rgba(0, 0, 0, 0.3);
+    margin-top: 12px;
+}
+
+.navbar-toggle span {
+    background: #fff;
+}
+
+.navbar .nav > li > a, .navbar .nav > .active > a, .navbar .nav > li > section {
+    padding: 18px 25px 19px;
+    background: none;
+    box-shadow: none;
+    text-shadow: none;
+    color: #fff;
+}
+.nav .nav-user-signin {
+    padding: 18px 25px 19px !important;
+}
+
+.navbar .nav > .active > a, .navbar .nav > .active > a:hover, .navbar .nav > .active > a:focus {
+    box-shadow: none;
+    color: #fff;
+}
+
+
+.navbar .nav > li > a:hover {
+    background: rgba(255, 255, 255, 0.1);
+    color: #fff;
+}
+
+.navbar .nav > li > a:focus {
+    background: #0678be;
+    color: #fff;
+}
+
+.navbar .nav {
+    float: right;
+    margin: 1px 0 0 0;
+    color: #fff;
+    font-family: 'Open Sans', sans-serif;
+    font-size: 14px;
+}
+
+.nav-user {
+    position: relative;
+}
+
+.navbar .nav > li > a.nav-signout {
+    padding: 19px 19px 15px;
+    font-size: 22px;
+}
+
+.navbar .nav > .nav-user > section {
+    cursor: pointer;
+}
+
+.navbar .nav > .nav-user > .nav-user-signin {
+    padding: 17px 20px;
+}
+
+.navbar .nav > .nav-user > section > .nav-user-menu, .navbar .nav > .nav-user > section > .signin-box {
+    display: none;
+}
+
+.navbar .nav > .nav-user > section:hover > .nav-user-menu, .navbar .nav > .nav-user > section:hover > .signin-box {
+    display: block;
+}
+
+@media (max-width: 767px) {
+    .navbar .nav { float: none; }
+    .navbar .nav > li > a,
+    .navbar .nav > .active > a {
+        border-bottom: 1px solid rgba(118, 111, 107, 0.69);
+    }
+    .navbar .nav > li:last-child > a {
+        border-bottom: 0;
+    }
+    .navbar .nav > .nav-user > section:hover > .nav-user-menu,
+    .navbar .nav > .nav-user > .nav-user-signin:hover > .signin-box {
+        display: none;
+    }
+}
+
+.navbar .nav > .nav-user > section a {
+    color: #fff;
+    text-decoration: none;
+    display: block;
+}
+
+.navbar .nav > .nav-user > section:hover {
+    background: rgba(255, 255, 255, 0.1);
+}
+
+.navbar .nav > .nav-user > section:hover > .nav-user-menu a:hover {
+    color: #0678be;
+}
+
+.nav-user section img {
+    width: 57px;
+    height: 57px;
+    margin-right: 15px;
+    margin-top: -5px;
+}
+
+.nav-user .nav-user-menu, .nav-user .signin-box {
+    z-index: 110;
+    background: #414145;
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.3);
+    top: 57px;
+    right: 0;
+    position: absolute;
+}
+.nav-user .nav-user-menu {
+    width: 220px;
+}
+
+.nav-user .nav-user-menu ul {
+    padding: 12px 15px 12px 20px;
+}
+
+.nav-user .nav-user-menu li {
+    line-height: 31px;
+}
+
+.nav-user .nav-user-menu a {
+    color: #fff;
+}
+
+.nav-user .nav-user-menu span {
+    float: right;
+    font-size: 20px;
+}
+
+.nav-user .nav-user-menu span.normal {
+    float: none;
+}
+
+.nav-user .nav-user-menu hr {
+    margin: 7px 0 5px;
+}
+
+.nav-user .signin-box {
+    width: 300px;
+    color: #fff;
+}
+
+.nav-user .signin-box form {
+    padding: 15px 20px 12px;
+}
+
+.nav-user .signin-box .input-group-addon {
+    background: #fff;
+    border-top-left-radius: 3px;
+    border-bottom-left-radius: 3px;
+}
+
+.nav-user .signin-box .form-control {
+    border-left: 0;
+    box-shadow: none;
+    border-radius: 3px;
+}
+
+.nav-user .signin-box div {
+    font-size: 13px;
+}
+
+.nav-user .signin-box .checkbox {
+    float: left;
+    margin-top: 4px;
+}
+
+.nav-user .signin-box .signin-box-buttons {
+    float: left;
+    width: 100%;
+}
+
+.nav-user .signin-box .signin-box-register {
+    background: #0678be;
+    float: left;
+    font-size: 14px;
+    font-weight: 600;
+    margin: 11px 0 0;
+    padding: 7px 21px;
+    width: 100%;
+    text-align: center;
+}
+
+.nav-user .signin-box label {
+    vertical-align: middle;
+}
+
+.nav-user .btn-success {
+    width: 42%;
+}
+
+.nav-user .btn-github {
+    font-weight: 600;
+    padding: 6px 12px 5px;
+    width: 56%;
+}
+
+.nav-user .btn-github span {
+    font-size: 20px;
+    margin: 0 4px 0 0;
+    vertical-align: bottom;
+    position: relative;
+    top: 2px;
+}
+
+
+/* overrides for dropdown menu on mobile */
+@media (max-width: 767px) {
+    .navbar .nav > li {
+        height: 57px;
+    }
+    .navbar-collapse.in {
+        overflow-y: visible;
+    }
+    .nav-user .nav-user-menu {
+        right: 0;
+    }
+    .nav-user-signin {
+        width: 100%;
+    }
+    .nav-user .signin-box {
+        left: auto;
+        right: 0;
+    }
+}
+
+
+
+.wrap {
+    min-height: 100%;
+    height: auto;
+    margin: 0 auto -135px;
+    padding: 0 0 135px;
+    position: relative;
+}
+
+.wrapper .container {
+    padding: 26px 20px;
+    color: #2d2d32;
+}
+.wrapper .container.flash-container {
+  padding-bottom: 0;
+}
+
+.content p {
+    font-size: 14px;
+    margin-top: 10px;
+    line-height: 22px;
+}
+
+.wrapper-search .container {
+    padding: 25px 20px 25px;
+}
+@media (min-width: 768px) {
+  .wrapper-search .container.with-description {
+    padding-left: 143px;
+  }
+}
+.wrapper-search .container p {
+  margin-top: 8px;
+  margin-bottom: 0;
+}
+
+.wrapper .container .btn-white {
+    margin: -3px 0 0 5px;
+}
+
+.wrapper-search {
+    background: #0678be;
+}
+
+.wrapper-search .container {
+    color: #fff;
+}
+
+.wrapper-search a {
+    color: #fff;
+    border-bottom: 1px dotted rgba(255, 255, 255, 0.6);
+}
+
+.wrapper-search a:hover {
+    text-decoration: none;
+}
+
+.wrapper-footer {
+    background: #2d2d32;
+    font-size: 14px;
+    line-height: 16px;
+    padding: 35px 0 20px;
+}
+footer p {
+  float: left;
+  color: #c3c3c3;
+}
+
+.wrapper-footer ul {
+    list-style: none;
+}
+
+.wrapper-footer li {
+    padding-bottom: 8px;
+}
+
+.wrapper-footer a {
+    color: #fff;
+}
+
+.wrapper-footer .social {
+    font-size: 26px;
+}
+
+.wrapper-footer .social li {
+    float: right;
+    margin-left: 15px;
+}
+
+
+#search-form {
+    margin-top: 5px;
+}
+
+#search-form input[type="search"] {
+    background: #FFF;
+    font: 18px 'Open Sans', sans-serif;
+    border: 0;
+    padding: 0px 10px;
+    color: #2d2d32;
+    outline: none;
+}
+
+
+button {
+    font-family: 'Open Sans', sans-serif;
+}
+
+.form-control {
+    min-height: 20px;
+    padding: 7px 11px 6px 9px;
+    box-shadow: none;
+    border-radius: 2px;
+    font-family: 'Open Sans', sans-serif;
+
+    transition: none;
+    -o-transition: none;
+    -moz-transition: none;
+    -webkit-transition: none;
+}
+
+.form-control[disabled] {
+    background: #fff;
+}
+
+.form-control:focus {
+    padding: 6px 9px 5px;
+    box-shadow: none;
+    border-width: 2px;
+    border-radius: 0 2px 2px 0;
+}
+
+.form-control[readonly] {
+    cursor: pointer;
+}
+
+.form-control[readonly]:focus {
+    padding: 7px 11px 6px 9px;
+    border-radius: 2px;
+    box-shadow: none;
+}
+
+.form-control[data-api-token] {
+    font-size: 16px;
+    height: 42px;
+    padding: 5px 11px 7px 9px;
+}
+
+input:focus:invalid:focus, textarea:focus:invalid:focus, select:focus:invalid:focus {
+    box-shadow: none;
+}
+
+.input-group {
+    display: inline;
+}
+
+.input-group .input-group-addon {
+    float: left;
+    min-height: 22px;
+    width: 10%;
+    line-height: 20px;
+    padding: 6px 0;
+    margin: 0 -4px 6px 0;
+    border-right: 0;
+    background: none;
+    color: #c6cacd;
+    font-size: 16px;
+}
+
+.input-group .form-control {
+    width: 90%;
+    float: right;
+    margin: 0 0 6px;
+}
+
+.api-token-group {
+    display: block;
+    min-height: 45px;
+}
+.api-token-group #api-token {
+  width: 85%;
+  position: absolute;
+  right: 0;
+  text-align: center;
+}
+.api-token-group .btn-show-api-token {
+  width: 145px;
+  position: absolute;
+  z-index: 10;
+}
+
+.input-group input.input-lg ~ .input-group-addon {
+    line-height: 32px;
+    font-size: 20px;
+}
+
+.input-group .input-group-addon-button {
+    padding: 0;
+}
+
+.input-group .input-group-addon .btn {
+    padding: 6px 20px;
+    border-width: 0 0 2px;
+}
+
+.input-group input:focus ~ .input-group-addon {
+    padding-top: 5px;
+    padding-bottom: 5px;
+    border: 2px solid rgba(82, 168, 236, 0.8);
+    border-right: 0;
+    border-radius: 2px 0 0 2px;
+}
+
+.input-group input:focus ~ .input-group-addon span {
+    margin-left: -1px;
+    color: rgb(82, 168, 236);
+}
+
+ul.packages {
+    margin-top: 20px;
+}
+
+ul.packages h1 {
+  font-family: Verdana;
+  font-size: 22px;
+  line-height: 1em;
+  font-weight: normal;
+  margin: 0;
+  padding: 8px 4px 0 0;
+  height: 32px;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
+}
+
+ul.packages .metadata {
+  color: #555;
+  font-size: 15px;
+}
+ul.packages .metadata .glyphicon {
+  top: 3px;
+  margin: 0 1px;
+  font-size: 18px;
+}
+ul.packages .metadata-block {
+  display: block;
+  margin-bottom: 10px;
+}
+ul.packages .metadata-block:last-child {
+  margin-bottom: 0;
+}
+
+.input-group input:focus:invalid:focus {
+    border-color: #fa3a15;
+}
+
+.input-group input:focus:invalid:focus ~ .input-group-addon {
+    border-color: #fa3a15;
+}
+
+.input-group input:focus:invalid:focus ~ .input-group-addon span {
+    color: #fa3a15;
+}
+
+.input-group input, .input-group select {
+    border-left: 0;
+}
+
+.input-group-addon:last-child, .input-group .btn:last-child {
+    border-radius: 2px 0 0 2px;
+}
+
+.input-group .form-control:first-child, .input-group-addon:first-child {
+    border-bottom-left-radius: 0;
+    border-top-left-radius: 0;
+}
+
+.input-group-addon:last-child {
+    border-left: 1px solid;
+    border-right: 0;
+}
+
+.nav-tabs {
+    border: 0;
+}
+
+.nav-tabs li a {
+    padding: 11px 20px;
+    background: #fff;
+    color: #515966;
+    font-weight: 600;
+    border-radius: 0;
+}
+
+.nav-tabs li:first-child a {
+    border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs li:last-child a {
+    border-radius: 0 0 4px 4px;
+}
+
+.nav-tabs li.active a, .nav-tabs.nav-stacked > li > a:hover {
+    background: #0678be;
+    color: #fff;
+}
+
+.nav-tabs.nav-stacked > li > a, .nav-tabs.nav-stacked > li > a:hover {
+    border: 0;
+}
+
+.nav-tabs.nav-stacked > li > a:hover {
+    background: #0678be;
+}
+
+.nav-tabs.nav-stacked [class^="icon-"], .nav-tabs.nav-stacked [class*=" icon-"] {
+    float: right;
+    font-size: 20px;
+    margin-top: -2px;
+}
+
+.nav-stacked>li+li {
+  margin-top: 0;
+}
+.nav-tabs>li{
+  margin-bottom: 0;
+}
+
+.btn {
+    padding: 9px 20px;
+    box-shadow: none;
+    border: 0;
+    border-bottom: 4px solid;
+    border-radius: 3px;
+    text-shadow: none;
+    font-weight: 600;
+}
+.btn-group-xs .btn {
+    padding: 5px 10px;
+    font-size: 12px;
+}
+
+.btn.loading, .btn.loading:hover, .btn.loading:active {
+    background-image: url("../img/loader.gif");
+    background-position: 95% 50%;
+    background-repeat: no-repeat;
+}
+
+.btn-default {
+    background: #bbbfc4;
+    border-bottom-color: #a5aab0;
+    color: #fff;
+    font-size: 14px;
+}
+.btn-default:focus, .btn-default:active {
+  color: #fff;
+  background: #a5aab0;
+  border-bottom-color: #bbbfc4;
+}
+
+.btn.disabled {
+    border-bottom-color: #e1e1e8;
+}
+
+.btn:hover {
+    background: #bbbfc4;
+    color: #fff;
+}
+
+.btn.disabled:hover {
+    background: #e6e6e6;
+    color: #333;
+}
+
+.btn-danger, .btn-danger:hover {
+    background: #ff4533;
+    border-bottom-color: #cd3729;
+}
+.btn-danger:focus, .btn-danger:active {
+    background: #cd3729;
+    border-bottom-color: #ff4533;
+}
+
+.btn-inverse, .btn-inverse:hover {
+    color: #fff;
+    background: #454e5c;
+    border-bottom-color: #303339;
+}
+.btn-inverse:focus, .btn-inverse:active {
+    color: #fff;
+    background: #303339;
+    border-bottom-color: #454e5c;
+}
+
+.btn-primary, .btn-primary:hover {
+  background: #3594e2;
+  border-bottom-color: #2f6cca;
+}
+.btn-primary:focus, .btn-primary:active {
+  background: #2f6cca;
+  border-bottom-color: #3594e2;
+}
+
+.btn-success, .btn-success:hover {
+    background: #83c129;
+    border-bottom-color: #69ad21;
+}
+.btn-success:focus, .btn-success:active {
+    background: #69ad21;
+    border-bottom-color: #83c129;
+}
+
+.btn-warning, .btn-warning:hover {
+    background: #ed9e2e;
+    border-bottom-color: #0678be;
+}
+.btn-warning:focus, .btn-warning:active {
+    background: #0678be;
+    border-bottom-color: #ed9e2e;
+}
+
+.btn-github {
+    line-height: 26px;
+    font-weight: 600;
+    color: #fff;
+    text-align: center;
+}
+
+.btn-github span {
+    margin: 0 15px 0 0;
+    vertical-align: text-top;
+    font-size: 22px;
+}
+
+/* Search */
+#search_query_query {
+  display: inline;
+}
+
+.wrapper-search {
+  clear: both;
+}
+.wrapper-search .sortable a {
+  border: 0;
+}
+
+#search_query_orderBys {
+  display: none;
+}
+
+.sortable .glyphicon.active {
+  color: #2d2d32;
+}
+
+.sortable #order-bys-wrapper {
+  font-size: 24px;
+  position: relative;
+  top: 2px;
+  margin-left: -12px;
+  padding-right: 0;
+  white-space: nowrap;
+  overflow: hidden;
+  height: 30px;
+}
+
+.sortable .order-by-group .icon:first-child {
+  margin-right: 5px;
+}
+
+.sortable #order-bys-wrapper a {
+  margin-right: 7px;
+  display: inline-block;
+}
+
+.sortable #order-bys-wrapper a.clear {
+  width: auto;
+  margin-right: 0px;
+  margin-left: 5px;
+}
+
+#search-form .submit-wrapper {
+  width: 100px;
+  float: right;
+  display: none;
+}
+.no-js #search-form .submit-wrapper {
+  display: block;
+}
+#search-form .submit {
+  margin: 0;
+  padding: 6px 20px;
+  width: 100px;
+}
+#search-form p {
+  margin: 0;
+}
+.search-list {
+  margin-top: 10px;
+}
+
+
+.package h2.title {
+    margin-bottom: -1px !important;
+    position: relative;
+}
+.package .action input.loading, .package .action input.loading:hover, .package .action input.loading:active {
+  background-position: 10px center;
+  background-image: url("../img/loader.gif");
+  background-repeat: no-repeat;
+  padding-left: 35px;
+}
+.package .action {
+  margin-right: 5px;
+  margin-bottom: 15px;
+  display: inline-block;
+}
+.package, .package p {
+    font-size: 14px;
+}
+
+.package p.description {
+    margin-top: 0;
+    font-size: 25px;
+    font-style: italic;
+    font-weight: 300;
+    margin-bottom: 15px;
+}
+@media (max-width: 767px) {
+    .package p.description {
+        font-size: 20px;
+    }
+}
+.package .details {
+    line-height: 200%;
+}
+.package .details p > i:first-child {
+    margin-right: 5px;
+}
+.package .details p, .package .facts p {
+    margin: 0;
+}
+.package .details .maintainers {
+  padding-bottom: 6px;
+  font-size: 0.0001px;
+}
+.package .details .maintainers img {
+  margin-bottom: 4px;
+  margin-right: 4px;
+}
+.package .details #add-maintainer {
+  margin-top: 3px;
+  margin-bottom: 7px;
+  float: right;
+  font-size: 34px;
+}
+.package .details #remove-maintainer {
+  float: right;
+  font-size: 35px;
+  margin-top: 5px;
+}
+.package .details .canonical {
+  text-overflow: ellipsis;
+  overflow: hidden;
+  width: 100%;
+  white-space: nowrap;
+}
+
+.package .facts {
+    margin-top: 10px;
+}
+@media (max-width: 991px) and (min-width: 768px) {
+    .package .facts {
+        margin-top: 0;
+    }
+}
+.package .facts span {
+    display: inline-block;
+    width: 100px;
+}
+
+.package .tags a:before {
+  content: "#";
+}
+.package .tags a {
+  margin-right: 10px;
+  display: inline-block;
+}
+
+.btn-github.disabled {
+    background: #ecf0f1;
+    color: #bdc3c7;
+}
+
+
+
+.package .readme {
+  padding-bottom: 20px;
+}
+.wrapper .content .package .readme img {
+  max-width: 100%;
+}
+.wrapper .content .package .readme h1:first-child,
+.wrapper .content .package .readme p:first-child,
+.wrapper .content .package .readme h2:first-child,
+.wrapper .content .package .readme h3:first-child {
+  margin-top: 0;
+}
+.wrapper .content .package .readme h1 {
+  font-size: 20px;
+  font-weight: 300;
+  padding: 0;
+  margin: 25px 0 10px;
+  line-height: 100%;
+}
+.wrapper .content .package .readme h2 {
+  font-size: 16px;
+  font-weight: 600;
+  padding: 0;
+  margin: 25px 0 10px;
+  line-height: 100%;
+}
+.wrapper .content .package .readme h3,
+.wrapper .content .package .readme h4,
+.wrapper .content .package .readme h5,
+.wrapper .content .package .readme h6 {
+  font-size: 14px;
+  font-weight: 600;
+  padding: 0;
+  margin: 15px 0 5px;
+  line-height: 100%;
+}
+
+.wrapper .content .package .readme table {
+  margin: 0 0 15px;
+}
+.wrapper .content .package .readme th,
+.wrapper .content .package .readme td {
+  border: 1px solid #CCC;
+  padding: 5px 10px;
+}
+.wrapper .content .package .readme th {
+  background: #EEE;
+}
+.wrapper .content .package .readme table code {
+  font-size: 13px;
+}
+
+.package .package-aside {
+  border-top: 1px solid #0678be;
+  border-radius: 0;
+
+  margin: 0;
+  padding: 20px 0 0;
+}
+.package .package-aside.versions-wrapper {
+  margin-left: 15px;
+  margin-right: 15px;
+}
+.package-aside .details {
+  padding-left: 0;
+}
+.package .package-aside i {
+  color: #bbbfc4;
+}
+.package .package-aside i:hover {
+  color: #a5aab0;
+}
+
+.package .details-toggler.open, .package .details-toggler.open a, .package .details-toggler.open i {
+  background: #0678be;
+  color: #fff;
+}
+
+.package .package-aside.versions-wrapper, .package .version-details {
+  margin-top: 20px;
+}
+.package .version-details .title {
+  border-top: 1px solid #0678be;
+  padding-top: 10px;
+}
+.package .versions {
+  list-style: none;
+  line-height: 170%;
+  overflow-y: hidden;
+  max-height: 300px;
+  padding: 0;
+  margin: 0;
+}
+.package .versions-expander {
+  text-align: center;
+  cursor: pointer;
+  margin: 10px 0 0;
+  line-height: 25px;
+}
+.package .versions .version {
+  padding: 2px 0 2px 5px;
+}
+.package .versions .version-number {
+  display: block;
+  float: left;
+  width: 91%;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
+.package .details-toggler {
+  cursor: pointer;
+}
+
+.package .versions .version {
+  position: relative;
+  display: block;
+  height: 26px;
+}
+.package .delete-version {
+  display: inline-block;
+}
+.package .delete-version .submit {
+  position: absolute;
+  right: 5px;
+  top: 7px;
+}
+.package .delete-version .submit:hover {
+  color: #cd3729;
+}
+
+@media (min-width: 992px) {
+    .package-aside .details {
+      padding-left: 15px;
+    }
+    .package .package-aside {
+      background: #fff;
+      border-radius: 2px;
+      border-top: 0;
+      padding: 10px 0;
+      margin-left: -15px;
+      margin-right: -15px;
+    }
+    .package .package-aside.versions-wrapper {
+      margin-left: 0;
+      margin-right: 0;
+      padding: 15px;
+    }
+    .package .versions {
+      margin: 0 -15px;
+    }
+    .package .versions .version {
+      padding: 2px 15px 2px 15px;
+    }
+    .package .delete-version .submit {
+      right: 15px;
+    }
+}
+@media (max-width: 767px) {
+  .package-aside .facts {
+    padding-left: 0;
+  }
+  .wrapper-footer {
+    font-size: 12px;
+  }
+  .wrapper-footer a {
+    white-space: nowrap;
+  }
+}
+
+.package .version-details .version-number {
+  font-size: 16px;
+  font-weight: 600;
+  color: #2d2d32;
+}
+.version-details.loading .title .version-number:after {
+  display: inline-block;
+  content: "";
+  width: 16px;
+  height: 16px;
+  background-image: url("../img/loader-white.gif");
+  background-repeat: no-repeat;
+  position: relative;
+  top: 2px;
+  left: 10px;
+}
+.package .version-details .release-date {
+  float: right;
+  position: relative;
+  top: 3px;
+  font-size: 12px;
+}
+.package .version-details .link-type {
+    font-weight: 600;
+    text-transform: lowercase;
+}
+.package i {
+    color: #2d2d32;
+}
+.package .version-details .source-reference {
+    margin-left: 10px;
+    display: inline-block;
+}
+.package .version-details .license {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.package .version-details .license .unknown, .abandoned i {
+  color: #c00;
+}
+.package .version-details .authors {
+  list-style: none;
+  display: inline;
+  margin: 0;
+  padding: 0;
+}
+.package .version-details .authors li {
+  display: inline;
+  margin-right: 5px;
+}
+.package .version-details .authors li:after {
+  content: ", ";
+}
+.package .version-details .authors li:last-child:after {
+  content: "";
+}
+.package .version-details .package-links {
+  border-top: 1px solid #0678be;
+  border-bottom: 1px solid #0678be;
+  margin-top: 10px;
+  padding-top: 10px;
+  padding-bottom: 10px;
+  margin-bottom: 20px;
+}
+.package .version-details .package-links ul  {
+    padding-left: 18px;
+}
+.package .version-details .package-links .no-links {
+    color: #999;
+}
+
+.package .requireme {
+    margin-top: 15px;
+}
+.package .requireme input {
+  cursor: text;
+  border: 0 !important;
+  border-radius: 0;
+  background-color: transparent;
+  font-family: Consolas, "Courier New", Courier, monospace;
+  min-width: 80%;
+  position: relative;
+  top: -3px;
+  left: 2px;
+}
+
+
+.package .mark-favorite {
+  font-size: 20px;
+  cursor: pointer;
+  color: #BDC3C7;
+  position: absolute;
+  left: -25px;
+  top: 15px;
+}
+.package .mark-favorite.is-starred {
+  color: #0678be;
+}
+@media (max-width: 767px) {
+    .package .mark-favorite {
+      left: auto;
+      right: 0;
+    }
+}
+
+.pagination {
+    margin: 6px 0;
+}
+
+.pagination ul {
+    list-style: none;
+    margin: 0;
+    padding-left: 0;
+}
+
+.pagination ul > li {
+    float: left;
+    line-height: 40px;
+    padding: 0;
+}
+
+.pagination ul > li > a, .pagination ul > li > span {
+    font-size: 14px;
+    font-weight: 600;
+    height: 40px;
+    text-align: center;
+    text-decoration: none;
+    padding: 0 10px;
+    display: inline-block;
+}
+.pagination ul > li > a:hover {
+    background: #F2F2F2;
+}
+
+.legend {
+  font-size: .8em;
+  margin-bottom: 10px;
+  text-align: center;
+}
+.legend li {
+  display: inline;
+  padding: 0 10px;
+}
+.legend span {
+  font-size: 1.5em;
+}
+.legend-first {
+  color: #0678be;
+}
+.legend-second {
+  color: #2d2d32;
+}
+
+.wrapper .content h2.title, .wrapper .content h3.title {
+    margin: -5px 0 15px;
+    padding: 0 0 15px;
+    border-bottom: 1px solid #0678be;
+    color: #2d2d32;
+}
+
+.wrapper .content h3.title {
+    padding: 0 0 15px;
+    font-size: 40px;
+    line-height: 45px;
+}
+.content h3.profile-title {
+    margin-top: 0;
+    margin-bottom: 20px;
+}
+
+.wrapper .content h2 {
+    font-size: 36px;
+    line-height: 44px;
+    padding-bottom: 18px;
+    padding-right: 20px;
+}
+
+.wrapper .content h2 small {
+    margin-left: 10px;
+    vertical-align: middle;
+}
+
+.wrapper .content h2 a {
+    color: #2d2d32;
+    font-weight: 600;
+    text-decoration: none;
+}
+
+@media (max-width: 767px) {
+    .wrapper .content h2.title {
+        font-size: 26px;
+        padding-bottom: 5px;
+    }
+}
+
+.wrapper .content p {
+    line-height: 150%;
+    vertical-align: baseline;
+}
+
+
+.font-slim {
+    font-weight: 300;
+}
+
+.font-normal {
+    font-weight: 400;
+}
+
+.font-bold {
+    font-weight: 500;
+}
+
+.font-fat {
+    font-weight: 600;
+}
+
+.text-center {
+    text-align: center;
+}
+
+
+.col-lg-6 .input-group input {
+    width: 89%;
+}
+
+.col-lg-6 .input-group .input-group-addon {
+    margin-right: -5px;
+    width: 8%;
+}
+
+
+#submit-package-form .form-control {
+    border-radius: 4px;
+}
+
+
+.fos_user_profile_edit .notifications {
+    margin: 10px 0;
+    float: left;
+}
+
+
+.packages-short ul {
+    list-style: none;
+    margin: 0;
+}
+
+.packages-short li {
+    margin-bottom: 12px;
+    font-style: italic;
+    font-weight: 400;
+}
+
+.packages-short li a {
+    display: block;
+    font-style: normal;
+}
+
+.packages-short li a strong {
+    margin-left: 5px;
+    font-size: 14px;
+}
+
+.packages .package-item {
+    padding: 5px 15px;
+    margin-bottom: 10px;
+    background: #fff;
+    border-radius: 4px;
+}
+
+.packages .selected .package-item {
+    padding: 4px 14px;
+    border: 1px solid #0678be;
+}
+
+.packages .package-item h4 {
+    font-size: 20px;
+    margin-bottom: 0;
+}
+
+.packages .package-item h4 small {
+    margin-left: 5px;
+    color: #83C129;
+    font-size: 16px;
+}
+
+.alert {
+    background: none;
+    border: none;
+    border-left: 4px solid #000;
+    box-shadow: none;
+    border-radius: 0;
+    padding: 5px 0 5px 10px;
+}
+.alert p {
+  margin: 0;
+}
+.flash-container .alert {
+  margin-bottom: 0;
+}
+.alert-warning {
+    border-left-color: #0678be;
+    color: #2d2d32;
+}
+.alert-danger, .alert-error {
+    border-left-color: #cd3729;
+    color: #2d2d32;
+}
+.alert-success {
+    border-left-color: #69AD21;
+    color: #2d2d32;
+}
+
+
+.chart {
+    min-height: 300px;
+}
+.version-stats-chart .chart {
+    min-height: 400px;
+}
+svg {
+    display: auto !important;
+    width: auto !important;
+    height: auto !important;
+}
+svg.chart {
+    display: block !important;
+    width:100% !important;
+    height:100% !important;
+    padding-left: 0;
+}
+@media (max-width: 767px) {
+  svg.chart {
+    margin-left: -10px;
+  }
+}
+.nvd3 text {
+  font: normal 12px 'Open Sans';
+}
+.nv-x text {
+  baseline-shift: -10px;
+}
+.nv-context .nv-x text {
+  baseline-shift: -5px;
+}
+.nvd3 .nv-axis .domain {
+  display: none;
+}
+.nvd3 .nv-axis .nv-axisMaxMin text {
+  font-weight: normal !important;
+}
+.nvd3 .nv-y .nv-axis .nv-axisMaxMin:last-child {
+  display: none;
+}
+.nvd3 .nv-x .nv-axis .nv-axisMaxMin:last-child text {
+  text-anchor: end !important;
+}
+.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
+  fill-opacity: 0 !important;
+  stroke-opacity: 0 !important;
+}
+
+@media (max-width: 1199px) {
+    .publishing-packages {
+        margin-top: 40px;
+    }
+}
+#how-to-submit-packages, #how-to-update-packages {
+    margin-top: 40px;
+}
+
+.package-installs dt {
+  float: left;
+  min-width: 125px;
+  text-align: right;
+  margin-right: 20px;
+}
+.package-installs dl {
+  margin-bottom: 5px;
+}
+.version-stats.package .package-aside.versions-wrapper {
+  margin-top: 0;
+}
+.version-stats-chart {
+  width: 70%;
+  margin-right: 5%;
+  position: relative;
+}
+
+
+[class^="icon-"]:before,
+[class*=" icon-"]:before {
+    font-family: 'fontello';
+    font-style: normal;
+    font-weight: normal;
+    font-size: 110%;
+
+    display: inline-block;
+    width: 1em;
+
+    line-height: 1;
+    text-decoration: inherit;
+    text-align: center;
+    speak: none;
+}
+
+.icon-tools:before { content: '\2692'; } /* '⚒' */
+.icon-cogs:before { content: '\26ef'; } /* '⛯' */
+.icon-mail:before { content: '\2709'; } /* '✉' */
+.icon-user-add:before { content: '\e700'; } /* '' */
+.icon-vcard:before { content: '\e722'; } /* '' */
+.icon-archive:before { content: '\e738'; } /* '' */
+.icon-share:before { content: '\e73c'; } /* '' */
+.icon-back-in-time:before { content: '\e771'; } /* '' */
+.icon-off:before { content: '\e78e'; } /* '' */
+.icon-flow-tree:before { content: '\e792'; } /* '' */
+.icon-paper-plane:before { content: '\e79b'; } /* '' */
+.icon-traffic-cone:before { content: '\e7a3'; } /* '' */
+.icon-github:before { content: '\f09b'; } /* '' */
+.icon-lightbulb:before { content: '\f0eb'; } /* '' */
+.icon-twitter:before { content: '\f309'; } /* '' */
+.icon-box:before { content: '\f330'; } /* '' */
+.icon-leaf:before { content: '🍂'; } /* '\1f342' */
+.icon-user:before { content: '👤'; } /* '\1f464' */
+.icon-users:before { content: '👥'; } /* '\1f465' */
+.icon-folder:before { content: '📁'; } /* '\1f4c1' */
+.icon-chart:before { content: '📈'; } /* '\1f4c8' */
+.icon-megaphone:before { content: '📣'; } /* '\1f4e3' */
+.icon-newspaper:before { content: '📰'; } /* '\1f4f0' */
+.icon-key:before { content: '🔑'; } /* '\1f511' */
+.icon-lock:before { content: '🔒'; } /* '\1f512' */
+.icon-lock-open:before { content: '🔓'; } /* '\1f513' */
+.icon-rocket:before { content: '🚀'; } /* '\1f680' */
+
+.deprecation-notice {
+    background: #FFBBBB;
+    color: #fff;
+    padding: 20px;
+}
+
+.deprecation-notice p {
+    color: #2d2d32;
+    font-size: 16px;
+    margin: 0;
+}
+
+.deprecation-notice h1 a:hover {
+    color: #0678be;
+}
diff --git a/src/DrupalPackagist/Bundle/Resources/public/font/fontello.eot b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.eot
new file mode 100644
index 0000000..56122d5
Binary files /dev/null and b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.eot differ
diff --git a/src/DrupalPackagist/Bundle/Resources/public/font/fontello.svg b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.svg
new file mode 100644
index 0000000..09bb3c7
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.svg
@@ -0,0 +1,138 @@
+<?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">
+<metadata>
+Created by FontForge 20100429 at Mon Feb 25 20:22:10 2013
+ By root
+Copyright (C) 2012 by original authors @ fontello.com
+</metadata>
+<defs>
+<font id="fontello" horiz-adv-x="1030" >
+  <font-face 
+    font-family="fontello"
+    font-weight="500"
+    font-stretch="normal"
+    units-per-em="1000"
+    panose-1="2 0 6 3 0 0 0 0 0 0"
+    ascent="850"
+    descent="-150"
+    bbox="14.5185 -142 1086 850"
+    underline-thickness="50"
+    underline-position="-100"
+    unicode-range="U+2692-1F680"
+  />
+<missing-glyph horiz-adv-x="364" 
+d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
+    <glyph glyph-name=".notdef" horiz-adv-x="364" 
+d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
+    <glyph glyph-name=".null" horiz-adv-x="0" 
+ />
+    <glyph glyph-name="nonmarkingreturn" horiz-adv-x="333" 
+ />
+    <glyph glyph-name="uni2692" unicode="&#x2692;" 
+d="M170 506q-14 -14 -14 -47q0 -11 -2 -11q-1 -1 -18 -16t-18 -16q-16 -13 -28 4l-70 76q-3 4 -4 8t-0.5 7t4 6.5t6 5.5t7.5 5.5t7 5.5q15 11 20 16q6 6 27 6q22 0 36.5 14.5t18.5 37.5q5 25 10 30q2 0 9 7q18 18 67 53q134 90 186 96q114 0 148 -2q13 0 -8 -8
+q-123 -54 -152 -76q-80 -56 -36 -114q35 -47 38 -48q8 -8 -2 -14q-8 -8 -38 -35t-38 -35q-14 -8 -18 -4q-42 48 -71 60t-67 -12zM866 4q17 -22 -2 -38l-48 -42q-22 -13 -38 4l-414 472q-8 8 0 20l72 62q13 8 20 -2zM994 516q-49 -88 -154 -62q-56 12 -100 -32l-82 -78
+l-68 78l68 70q40 40 37 118q-1 34 5 58q12 56 140 112q12 6 18 -3t2 -15q-10 -10 -46 -80q-13 -10 -12 -35q1 -24 40 -53q58 -40 96 22q2 4 24.5 38t23.5 36q4 10 13 9q10 -2 11 -17q16 -103 -16 -166zM152 -2l254 248l76 -86l-246 -242q-20 -20 -38 -4l-46 46q-23 18 0 38z
+" />
+    <glyph glyph-name="uni26EF" unicode="&#x26ef;" horiz-adv-x="1101" 
+d="M15 402q0 15 13 17l86 14q6 19 18 42q-23 32 -50 64q-4 5 -4 11q0 7 4 11q11 15 45 49t45 34q7 0 12 -4l64 -50q20 11 43 18q5 53 13 86q4 13 16 13h104q15 0 17 -14l13 -85q22 -7 41 -17l66 49q4 4 11 4t12 -4q81 -73 81 -90q0 -5 -4 -10l-23 -30q-21 -27 -26 -34
+q13 -25 19 -46l85 -12q13 -2 13 -17v-103q0 -15 -13 -17l-86 -14q-6 -19 -18 -42q23 -32 50 -64q4 -5 4 -11q0 -7 -4 -11q-14 -17 -46.5 -50t-43.5 -33q-6 0 -11 4l-64 50q-24 -12 -43 -17q-6 -55 -13 -87q-4 -13 -17 -13h-104q-14 0 -16 14l-13 85q-24 7 -42 17l-66 -49
+q-4 -4 -11 -4t-12 4q-80 73 -80 90q0 5 4 10l24 32l25 32q-14 25 -20 46l-85 13q-13 2 -13 16v103zM229 350q0 -58 42.5 -100.5t100.5 -42.5t100.5 42.5t42.5 100.5t-42.5 100.5t-100.5 42.5t-100.5 -42.5t-42.5 -100.5zM855 -77q-5 -9 -27 -37t-27 -28l-7 4l-26 15l-36 21
+q-3 3 -3 4q0 13 29 77q-11 14 -17 29q-83 9 -83 17v78q0 9 83 18q5 11 17 29q-29 64 -29 77q0 2 1 3.5t3 2.5t4.5 2t6.5 3t7 4l16 10q11 6 17 9q15 9 17 9q5 0 27 -28t27 -36q7 1 17 1t17 -1q33 45 51 63l4 1q2 0 69 -39q2 -2 2 -4q0 -15 -28 -77q9 -13 16 -29q83 -9 83 -18
+v-78q0 -8 -83 -17q-7 -17 -16 -29q28 -62 28 -77q0 -2 -2 -4l-37 -21l-25 -15l-7 -4q-5 0 -27.5 28.5t-27.5 36.5q-14 -2 -17 -2t-17 2zM855 494q-5 -8 -27 -36t-27 -28q-1 0 -69 39q-3 3 -3 4q0 13 29 77q-11 14 -17 29q-83 9 -83 18v78q0 8 83 17q5 11 17 29
+q-29 64 -29 77q0 2 1 3.5t3 2.5t4.5 2t6.5 3t7 4l16 9q11 7 17 10q15 9 17 9q5 0 27 -28t27 -36q14 2 17 2t17 -2q33 45 51 63l4 1q2 0 69 -39q2 -2 2 -4q0 -15 -28 -77q9 -13 16 -29q83 -9 83 -17v-78q0 -9 -83 -18q-7 -17 -16 -29q28 -62 28 -77q0 -2 -2 -4
+q-67 -39 -69 -39q-5 0 -27.5 28t-27.5 36q-7 -1 -17 -1t-17 1zM801 64q0 -29 21 -50t50 -21t50.5 21t21.5 50q0 28 -22 50t-50 22t-49.5 -21.5t-21.5 -50.5zM801 636q0 -29 21 -50.5t50 -21.5t50.5 21.5t21.5 50.5q0 28 -21.5 49.5t-50.5 21.5q-28 0 -49.5 -21.5
+t-21.5 -49.5z" />
+    <glyph glyph-name="uni2709" unicode="&#x2709;" horiz-adv-x="930" 
+d="M17 626q2 14 26 14h846q38 0 20 -32q-8 -15 -24 -22l-97 -51l-180 -97q-93 -50 -97 -52q-16 -10 -46 -10q-29 0 -46 10q-4 2 -97 52l-180 97l-97 51q-32 18 -28 40zM895 486q20 11 20 -10v-368q0 -16 -17 -32t-33 -16h-800q-16 0 -33 16t-17 32v368q0 21 20 10l384 -200
+q17 -10 46 -10t46 10z" />
+    <glyph glyph-name="uniE700" unicode="&#xe700;" 
+d="M815 6v-106h-800v202q41 16 82 26q95 34 129 69q35 35 35 95q0 22 -23 48q-24 27 -31 74q-1 11 -23 25q-21 13 -25 61q0 32 14 38l4 4q-1 6 -3 19q-13 53 -8 95.5t39 85.5q46 58 160 58t160 -58t42 -112l-14 -88q18 -8 18 -42q-5 -55 -23 -60q-20 -5 -23 -26
+q-10 -46 -33 -73q-23 -28 -23 -49q0 -60 36 -95t130 -69q180 -65 180 -122zM865 400h150v-100h-150v-150h-100v150h-150v100h150v150h100v-150z" />
+    <glyph glyph-name="uniE70A" unicode="&#xe70a;" 
+d="M26 353q74 120 203.5 201.5t285.5 81.5t285.5 -81.5t203.5 -201.5q11 -20 11 -39q0 -18 -11 -38q-74 -120 -203 -201.5t-286 -81.5q-156 0 -285.5 81.5t-203.5 201.5q-11 20 -11 38q0 19 11 39zM86 314q70 -109 180 -179.5t249 -70.5t249 70.5t180 179.5
+q-85 130 -213 197q34 -58 34 -125q0 -104 -73 -177t-177 -73t-177 73t-73 177q0 67 34 125q-128 -67 -213 -197zM345 386q0 -11 8 -19t19 -8t19 8t8 19q0 47 34.5 81.5t81.5 34.5q11 0 19 8t8 19t-8 18.5t-19 7.5q-69 0 -119.5 -50t-50.5 -119z" />
+    <glyph glyph-name="uniE70B" unicode="&#xe70b;" 
+d="M26 353q85 130 212 207q125 76 277 76q43 0 100 -10l31 54q6 9 15 9q4 0 28 -12l46 -27q9 -6 9 -15v-5l-176 -316l-176 -316l-28 -50q-6 -9 -15 -9q-7 0 -75 39q-9 6 -9 16q0 5 25 49q-162 74 -264 233q-11 19 -11 38q0 20 11 39zM368 184q-45 33 -74 85.5t-29 116.5
+q0 67 34 125q-128 -67 -213 -197q92 -142 239 -209zM345 386q0 -11 8 -19t19 -8t19 8t8 19q0 47 34.5 81.5t81.5 34.5q11 0 19 8t8 19t-8 18.5t-19 7.5q-69 0 -119.5 -50t-50.5 -119zM556 67q124 11 224 79t164 168q-63 98 -158 164l35 63q53 -36 102.5 -86t80.5 -102
+q11 -20 11 -39q0 -18 -11 -38q-42 -70 -111 -132t-144 -98q-111 -53 -234 -53zM604 152l157 281q4 -23 4 -47q0 -82 -46.5 -145t-114.5 -89z" />
+    <glyph glyph-name="uniE722" unicode="&#xe722;" 
+d="M915 750q42 0 71 -29t29 -71v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-800q-40 0 -70 30t-30 70v600q0 41 29.5 70.5t70.5 29.5h800zM915 650h-800v-600h800v600zM465 156h-250v90h250v-90zM465 306h-250v90h250v-90zM465 456h-250v90h250v-90zM811 226l4 -70h-250
+q0 70 6 70q84 22 84 66q0 16 -27 56t-27 88q0 110 90 110t90 -110q0 -48 -28 -88t-28 -56q0 -20 20.5 -35.5t43.5 -22.5z" />
+    <glyph glyph-name="uniE738" unicode="&#xe738;" horiz-adv-x="1011" 
+d="M159 600q0 18 11.5 33t26.5 17h608q20 0 36 -15q14 -14 14 -35v-50h-696v50zM259 700q0 18 11.5 33t26.5 17h410q20 0 36 -15q14 -14 14 -35h-498zM955 550q33 -30 38 -46q6 -18 0 -54l-76 -450q-3 -18 -17.5 -32.5t-30.5 -17.5h-710q-52 0 -60 50q0 1 -39 225t-39 225
+q-10 22 -3 44t10 26q1 2 21 20l40 40v-80h836v80zM707 280v100h-70v-80h-260v80h-68v-100q0 -50 48 -50h300q18 0 33 10.5t15 25.5z" />
+    <glyph glyph-name="uniE73C" unicode="&#xe73c;" horiz-adv-x="830" 
+d="M665 200q62 0 106 -43.5t44 -106.5q0 -62 -44.5 -106t-105.5 -44t-105.5 44t-44.5 106q0 3 1 13t1 13l-260 156q-43 -32 -92 -32q-61 0 -105.5 44t-44.5 106t44.5 106t105.5 44q55 0 92 -30l260 156q0 2 -1 12t-1 12q0 62 44.5 106t105.5 44q62 0 106 -43.5t44 -106.5
+q0 -62 -44.5 -106t-105.5 -44q-52 0 -90 32l-262 -156q2 -10 2 -26q0 -14 -2 -24l262 -156q36 30 90 30z" />
+    <glyph glyph-name="uniE771" unicode="&#xe771;" horiz-adv-x="970" 
+d="M836 640q119 -121 119 -290t-119 -290q-118 -120 -289 -120q-139 0 -252 88l70 76q83 -60 182 -60q126 0 216 90t90 216q0 128 -90 218t-216 90q-124 0 -213 -86q-88 -86 -93 -210h142l-184 -206l-184 206h124q5 166 123 282q119 116 285 116q171 0 289 -120zM511 570h70
+v-204l130 -130l-50 -50l-150 150v234z" />
+    <glyph glyph-name="uniE78E" unicode="&#xe78e;" horiz-adv-x="887" 
+d="M15 350q0 109 47.5 197t123.5 145q28 17 55 13t45 -27.5t14 -52.5t-28 -47q-114 -87 -114 -228q0 -118 84 -202t202 -84t202 84q83 83 83 202q0 141 -114 228q-22 17 -27 46t13 54q16 23 45.5 27.5t54.5 -13.5q76 -58 123.5 -145.5t47.5 -196.5q0 -91 -35 -169.5
+t-90 -133.5q-56 -56 -134.5 -91t-168.5 -35t-168.5 35t-134.5 91t-91 134.5t-35 168.5zM372 421v358q0 28 21.5 49.5t50.5 21.5q28 0 49.5 -21t21.5 -50v-358q0 -29 -21.5 -50t-49.5 -21q-29 0 -50.5 21.5t-21.5 49.5z" />
+    <glyph glyph-name="uniE792" unicode="&#xe792;" horiz-adv-x="970" 
+d="M485 820q49 0 84.5 -35t35.5 -85q0 -76 -72 -110v-140q0 -52 78 -52h100q85 0 129 -54q43 -53 43 -118v-114q72 -34 72 -112q0 -50 -35.5 -85t-84.5 -35t-84.5 35t-35.5 85q0 78 72 112v114q0 78 -76 78h-100q-43 0 -78 12v-204q72 -34 72 -112q0 -50 -35.5 -85
+t-84.5 -35t-84.5 35t-35.5 85q0 78 72 112v204q-30 -12 -76 -12h-100q-35 0 -56.5 -22t-21.5 -56v-114q72 -34 72 -112q0 -50 -35.5 -85t-84.5 -35t-84.5 35t-35.5 85q0 78 72 112v114q0 65 43 118q44 54 131 54h100q76 0 76 52v140q-72 34 -72 110q0 50 35.5 85t84.5 35z
+M205 0q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5q0 -28 20 -48t48 -20t49 20t21 48zM417 700q0 -28 20 -48t48 -20t49 20t21 48q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5zM555 0q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5
+q0 -28 20 -48t48 -20t49 20t21 48zM835 -68q28 0 49 20t21 48q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5q0 -28 20 -48t48 -20z" />
+    <glyph glyph-name="uniE79B" unicode="&#xe79b;" horiz-adv-x="951" 
+d="M909 720q13 4 21.5 -3t4.5 -19q-3 -8 -73 -313.5t-73 -312.5q-2 -13 -13 -18.5t-25 0.5l-139 75l-139 75l22 26l209 227l145 156q38 41 40 43q4 4 -1 9t-9 1l-550 -402l-302 120q-12 4 -12 12t12 12q7 4 227 82l437 154q217 76 218 76zM327 -8v204l160 -82
+q-122 -108 -142 -128q-18 -14 -18 6z" />
+    <glyph glyph-name="uniE7A3" unicode="&#xe7a3;" horiz-adv-x="991" 
+d="M495 432q58 0 108.5 16.5t61.5 47.5q2 -6 21 -58l35 -100q-5 -38 -70 -65q-66 -27 -156 -27t-156 27q-65 27 -70 65l35 100q19 52 21 58q11 -31 62.5 -47.5t107.5 -16.5zM371 624l52 142q10 34 72 34t74 -34q11 -34 50 -142q-29 -44 -124 -44t-124 44zM935 196
+q41 -16 41 -41.5t-37 -46.5l-352 -188q-37 -22 -91 -22t-91 22l-354 188q-38 20 -36 46t42 42l188 76l-22 -60q0 -47 80 -82q78 -34 192 -34t192 34q81 35 82 82l-22 60z" />
+    <glyph glyph-name="uniF09B" unicode="&#xf09b;" horiz-adv-x="887" 
+d="M15 350q0 123 60 219.5t153 151.5q101 58 216 58q123 0 219.5 -60t151.5 -154q57 -99 57 -215q0 -123 -60 -219.5t-153 -151.5q-101 -58 -215 -58q-123 0 -220 60t-152 154q-57 99 -57 215zM86 350q0 -115 70 -211q69 -95 180 -129v94q-27 -4 -38 -4q-61 0 -85 56
+q-11 29 -32 46q-23 19 -23 22q0 7 15 7q10 0 18.5 -2.5t17 -10t11.5 -10.5t12 -16l9 -12q21 -28 54 -28q21 0 45 8q9 30 35 49q-91 9 -137 47q-44 37 -44 125q0 67 40 111q-8 26 -8 47q0 30 16 60q33 0 56 -10q22 -9 56 -34q40 10 95 10q45 0 85 -9q31 22 56 33q23 10 56 10
+q15 -29 15 -60q0 -23 -8 -47q41 -47 41 -111q0 -89 -45 -126q-44 -37 -137 -46q39 -25 39 -73v-126q111 34 180 129q70 96 70 211q0 75 -29.5 141t-75.5 112t-111 75t-141 29t-141.5 -29t-111.5 -75t-75.5 -112t-29.5 -141z" />
+    <glyph glyph-name="uniF0EB" unicode="&#xf0eb;" horiz-adv-x="601" 
+d="M219 -25q-26 0 -43.5 14.5t-17.5 39.5q0 13 7 26q-25 16 -25 45q0 20 14 36q-14 16 -14 35q0 31 26 46q-4 52 -52 113q-21 27 -42 49q-57 63 -57 150q0 57 25.5 104.5t64.5 77.5q86 68 196 68q109 0 195 -68q39 -30 64.5 -77.5t25.5 -104.5q0 -87 -57 -150
+q-29 -32 -43.5 -51t-31.5 -51q-18 -34 -19 -60q26 -15 26 -46q0 -19 -14 -35q14 -16 14 -36q0 -29 -25 -45q8 -14 8 -26q0 -25 -17.5 -39.5t-43.5 -14.5q-10 -23 -32 -38.5t-50 -15.5t-50 15.5t-32 38.5zM124 428q3 -3 17 -18t17 -19q71 -85 79 -166h127q8 81 79 166
+q3 4 17 19t17 18q38 44 38 101q0 63 -39.5 105t-98.5 61q-37 12 -76 12q-80 0 -145 -47q-70 -51 -70 -131q0 -57 38 -101zM283 600q0 8 5.5 13t12.5 5q65 0 104 -39q21 -21 21 -50q0 -7 -5 -12.5t-13 -5.5t-13 5.5t-5 12.5q0 25 -30 39q-31 14 -59 14q-7 0 -12.5 5t-5.5 13z
+" />
+    <glyph glyph-name="uniF309" unicode="&#xf309;" horiz-adv-x="950" 
+d="M935 636q-37 -56 -94 -98v-24q0 -130 -60 -250q-60 -121 -186 -203q-127 -83 -290 -83q-159 0 -290 84q18 -2 46 -2q131 0 234 80q-64 2 -111.5 39t-64.5 93q9 -4 34 -4q26 0 50 6q-63 13 -106.5 64.5t-43.5 121.5v2q36 -20 84 -24q-84 58 -84 158q0 47 26 94
+q155 -189 390 -196q-6 18 -6 42q0 78 55.5 133t134.5 55q82 0 136 -58q58 12 120 44q-19 -66 -82 -104q55 8 108 30z" />
+    <glyph glyph-name="uniF330" unicode="&#xf330;" horiz-adv-x="990" 
+d="M301 806l194 -158l-284 -184l-196 164zM681 54l116 78v-46l-302 -190l-302 190v46l118 -78q6 -2 12 -2q10 0 14 4l158 132l160 -132q4 -4 14 -4q6 0 12 2zM975 628l-194 -164l-286 184l196 158zM495 286l286 178l174 -140l-282 -184zM319 140l-282 184l174 140l284 -178z
+" />
+    <glyph glyph-name="u1F342" unicode="&#x1f342;" horiz-adv-x="970" 
+d="M251 646q182 107 506 66q169 -23 196 -50q4 -5 -2 -10q-76 -40 -130 -109t-78 -132q-23 -61 -65 -132q-40 -69 -93 -105q-137 -96 -382 -4q-67 -78 -114 -176q-12 -24 -47 -7t-25 39q43 100 129 193q87 95 176 153q179 119 317 174l54 20q-65 0 -148.5 -15.5
+t-144.5 -37.5q-70 -25 -162 -84q-89 -57 -161 -141q-23 242 174 358z" />
+    <glyph glyph-name="u1F464" unicode="&#x1f464;" horiz-adv-x="970" 
+d="M751 128q204 -72 204 -122v-106h-940v106q0 50 204 122q93 34 128 69q34 34 34 95q0 23 -22 49q-21 26 -32 73q-3 21 -23 26q-18 5 -23 60q0 32 14 38l4 4q-7 46 -12 88q-4 53 41 112q45 58 157 58t158 -58t40 -112l-12 -88q18 -8 18 -42q-5 -55 -23 -60q-20 -5 -23 -26
+q-7 -47 -31 -74q-23 -26 -23 -48q0 -60 35 -95q34 -34 127 -69z" />
+    <glyph glyph-name="u1F465" unicode="&#x1f465;" 
+d="M791 -90v150q0 54 -30 81q-32 29 -154 89q40 30 40 84q0 17 -13 33t-19 51q-2 8 -14 16q-13 8 -14 42q0 24 12 30q-10 40 -8 73.5t23 64.5q27 40 95 40t96 -40q29 -40 24 -78l-8 -60q12 -6 12 -30q-1 -34 -14 -42q-12 -8 -14 -16q-6 -35 -19 -51t-13 -33q0 -42 21 -66
+t77 -48q113 -47 130 -80q6 -7 9 -61q4 -71 5 -101v-48h-224zM527 172q182 -78 182 -124v-138h-694v184q0 44 84 78q73 30 104 64q28 31 28 88q0 21 -19 44t-25 68q-1 10 -18 22q-15 11 -20 56q0 26 10 36l4 2l-5 46l-5 36q-5 50 33 103t127 53t127 -53t33 -103l-10 -82
+q14 -8 14 -38q-5 -45 -20 -56q-17 -12 -18 -22q-6 -45 -25 -68t-19 -44q0 -57 28 -88q31 -34 104 -64z" />
+    <glyph glyph-name="u1F4B3" unicode="&#x1f4b3;" 
+d="M915 700q41 0 70.5 -29.5t29.5 -70.5v-500q0 -42 -29 -71t-71 -29h-800q-41 0 -70.5 29.5t-29.5 70.5v500q0 40 30 70t70 30h800zM915 400h-800v-300h800v300zM915 600h-800v-50h800v50zM245 294v-30h-30v30h30zM425 234v30h30v30h60v-30h-30v-30h-30v-30h-60v30h30z
+M485 204v30h30v-30h-30zM305 204v30h60v-30h-60zM395 234h-30v60h60v-30h-30v-30zM275 234v-30h-60v30h30v30h30v30h60v-30h-30v-30h-30z" />
+    <glyph glyph-name="u1F4C1" unicode="&#x1f4c1;" horiz-adv-x="1031" 
+d="M969 500q29 0 38.5 -10.5t7.5 -37.5l-42 -452q-2 -27 -13.5 -38.5t-40.5 -11.5h-806q-51 0 -56 50l-42 452q-2 27 7.5 37.5t38.5 10.5h908zM935 610l10 -40h-846l14 132q4 19 20 33.5t36 14.5h164q52 0 86 -34l30 -30q31 -36 86 -36h340q20 0 38 -12t22 -28z" />
+    <glyph glyph-name="u1F4C8" unicode="&#x1f4c8;" horiz-adv-x="1033" 
+d="M49 284q-42 11 -32 56q11 42 54 32l98 -24l-52 -80zM939 272q14 12 33.5 11t30.5 -15q33 -32 -2 -64l-252 -226q-12 -12 -30 -12q-15 0 -28 10l-286 220l-54 14l50 80l36 -8q13 -5 16 -8l264 -204zM449 492l-350 -550q-12 -22 -38 -22q-25 0 -39 24t1 46l374 588
+q7 15 28 20q18 6 36 -6l246 -156l226 326q10 16 27.5 19t34.5 -9q38 -24 12 -62l-252 -362q-24 -36 -62 -12z" />
+    <glyph glyph-name="u1F4E3" unicode="&#x1f4e3;" horiz-adv-x="890" 
+d="M873 242q9 -120 -39 -140q-28 -12 -61 3q-36 16 -65 40q-37 28 -106.5 42t-141.5 7q-26 -4 -41.5 -18t-6.5 -38q26 -65 46 -108q4 -10 24 -22q19 -12 24 -20q13 -34 -22 -46q-45 -20 -102 -40q-30 -10 -54 42q-23 56 -58 132q-6 13 -34 17q-27 4 -46 31l-20 -7
+q-14 -5 -19 -6.5t-16 -3.5t-18 -0.5t-17 5t-22 10.5q-40 24 -54 60q-16 30 -5 79q11 47 43 61q126 52 213 108q84 54 124 103q34 42 59 92q22 44 25 78q7 76 51 95q48 19 130 -70t142 -228q58 -137 67 -258zM778 200q9 5 10 38q2 38 -11 98q-27 133 -108 251q-40 58 -67 84
+q-29 27 -36 23q-9 -5 -10 -42q-2 -40 10 -105q25 -141 108 -252q44 -57 68 -76q29 -23 36 -19z" />
+    <glyph glyph-name="u1F4F0" unicode="&#x1f4f0;" horiz-adv-x="830" 
+d="M715 800q42 0 71 -29t29 -71v-700q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-40 0 -70 30t-30 70v700q0 41 29.5 70.5t70.5 29.5h600zM715 700h-600v-700h600v700zM465 200h-250v50h250v-50zM615 400h-200v50h200v-50zM415 600h200v-100h-200v100zM365 400h-150v200h150v-200
+zM315 300h-100v50h100v-50zM365 350h250v-50h-250v50zM615 100h-400v50h400v-50zM515 200v50h100v-50h-100z" />
+    <glyph glyph-name="u1F511" unicode="&#x1f511;" horiz-adv-x="810" 
+d="M761 397q-48 -99 -150 -117q-67 -12 -130 -2l-118 -194l-70 -12l-104 -166q-13 -27 -46 -32l-76 -14q-13 -4 -22.5 4.5t-11.5 21.5l-16 98q-8 30 12 56l258 386q-25 51 -38 120q-10 57 7 108t52.5 87t81.5 60t97 33q107 20 195 -45q89 -65 107 -177q20 -117 -28 -215z
+M663 536q30 49 20 100t-50 80q-53 37 -109 17.5t-75 -72.5q-4 -10 4 -36q4 -13 31 -32q12 -8 67 -49q6 -5 42 -28q13 -8 19 -8q32 0 51 28z" />
+    <glyph glyph-name="u1F512" unicode="&#x1f512;" horiz-adv-x="673" 
+d="M69 -7q-23 0 -38.5 15.5t-15.5 37.5v322q0 22 15.5 37.5t38.5 15.5h17v108q0 102 74 176t176 74q103 0 177 -74q73 -73 73 -176v-108h18q23 0 38.5 -15.5t15.5 -37.5v-322q0 -22 -15.5 -37.5t-38.5 -15.5h-535zM336 671q-58 0 -100 -41.5t-42 -100.5v-108h285v108
+q0 58 -42 100t-101 42zM265 279q0 -44 39 -64l-38 -128q-3 -9 2.5 -16t14.5 -7h107q9 0 14.5 7t2.5 16l-38 128q39 20 39 64q0 29 -21.5 50t-50.5 21t-50 -21t-21 -50z" />
+    <glyph glyph-name="u1F513" unicode="&#x1f513;" horiz-adv-x="959" 
+d="M69 -7q-23 0 -38.5 15.5t-15.5 37.5v322q0 22 15.5 37.5t38.5 15.5h375v108q0 103 73 176q74 74 177 74q102 0 176 -74t74 -176v-143q0 -14 -11 -25t-25 -11h-36q-14 0 -25 11t-11 25v143q0 59 -42 100.5t-100 41.5q-59 0 -101 -42t-42 -100v-108h53q23 0 38.5 -15.5
+t15.5 -37.5v-322q0 -22 -15.5 -37.5t-38.5 -15.5h-535zM265 82q0 -8 5.5 -13t12.5 -5h107q8 0 13 5t5 13q0 4 -9 32l-19 64l-11 37q39 19 39 64q0 29 -21.5 50t-50.5 21t-50 -21t-21 -50q0 -44 39 -64l-1 -4q-1 -4 -4 -13l-6 -20q-3 -9 -20 -64q-8 -25 -8 -32z" />
+    <glyph glyph-name="u1F680" unicode="&#x1f680;" horiz-adv-x="890" 
+d="M558 236q7 -56 8 -81q2 -33 -8 -59q-4 -10 -7 -22t-4.5 -16t-6 -10t-11 -11t-19.5 -13l-33 -20q-11 -6 -16 -8.5t-27 -11.5l-39 -17q-46 -21 -85 -37q-30 -11 -44.5 3.5t-3.5 44.5l40 110l-130 132l-106 -40q-30 -13 -43.5 2.5t-2.5 45.5l16 42l25 62q12 29 17 40
+q37 75 47 81q2 1 12 8l17 12q17 13 41 13h52l71 -6q37 52 106 124q64 66 122.5 107.5t140.5 71.5q85 31 165 19q17 0 20 -20q10 -85 -18.5 -170.5t-75.5 -148.5q-99 -133 -184 -199zM608 532q22 -22 54 -22t54 23t22 55t-22 55t-54 23t-54 -23t-22 -55q0 -33 22 -56z" />
+  </font>
+</defs></svg>
diff --git a/src/DrupalPackagist/Bundle/Resources/public/font/fontello.ttf b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.ttf
new file mode 100644
index 0000000..dc72d13
Binary files /dev/null and b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.ttf differ
diff --git a/src/DrupalPackagist/Bundle/Resources/public/font/fontello.woff b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.woff
new file mode 100644
index 0000000..551266f
Binary files /dev/null and b/src/DrupalPackagist/Bundle/Resources/public/font/fontello.woff differ
diff --git a/src/DrupalPackagist/Bundle/Resources/public/img/drupal-packagist.svg b/src/DrupalPackagist/Bundle/Resources/public/img/drupal-packagist.svg
new file mode 100644
index 0000000..7e2d89e
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/img/drupal-packagist.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="139px" height="149px" viewBox="0 0 139 149" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <title>Drupal Packagist</title>
+    <desc>Combined icon of
+       Drop: Created by Chris Evans from the Noun project - This icon is licensed as Creative Commons – Attribution (CC BY 3.0) - http://thenounproject.com/term/drop/31491/
+       Package: Created by Peter Costello from the Noun project - This icon is licensed as Public Domain - http://thenounproject.com/term/box/9152/
+    </desc>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="Drupal-Packagist" sketch:type="MSLayerGroup">
+            <g id="Package" transform="translate(0.000000, 31.000000)" sketch:type="MSShapeGroup">
+                <path d="M137.778203,43.4698305 L126.882304,27.8325523 C126.839494,27.770516 126.77735,27.7222655 126.729016,27.664365 C126.657205,27.5775142 126.589538,27.4906634 126.50806,27.4120841 C126.476298,27.3831339 126.450059,27.3500479 126.418297,27.3224762 C126.350629,27.2645757 126.273294,27.2245967 126.201483,27.1735891 C126.135197,27.1280958 126.082719,27.0688167 126.01229,27.0274592 C125.975003,27.0054019 125.932193,26.9985089 125.894907,26.9778302 C125.88524,26.9736944 125.878335,26.9654229 125.868668,26.9612872 C125.839668,26.9461227 125.818953,26.9226868 125.788572,26.908901 L70.0910566,0.802655819 C69.9847215,0.753026795 69.8742435,0.717183611 69.7651464,0.682719011 C69.7389078,0.674447507 69.7140503,0.662040251 69.6878117,0.653768747 C69.6256678,0.635847155 69.5607619,0.624818483 69.498618,0.612411227 C69.4489029,0.601382555 69.4005688,0.591732467 69.3508536,0.583460963 C69.2597092,0.569675123 69.1699458,0.557267867 69.0788014,0.553132115 C69.0332292,0.550374947 68.987657,0.551753531 68.9407038,0.550374947 C68.875798,0.550374947 68.8095111,0.543482027 68.7432243,0.547617779 C68.7390814,0.547617779 68.7335575,0.548996363 68.7294145,0.548996363 C68.6023648,0.557267867 68.4794579,0.577946627 68.3565511,0.602761139 C68.3413603,0.605518307 68.3275506,0.605518307 68.3123598,0.609654059 C68.2543589,0.622061315 68.2005008,0.644118659 68.1438808,0.660661667 C68.0955467,0.674447507 68.0499745,0.688233347 68.0030213,0.704776355 C67.906353,0.737862371 67.8138276,0.777841307 67.7226832,0.820577411 C67.7005876,0.831606083 67.67573,0.834363251 67.6522534,0.845391923 L14.7139281,28.0062538 C14.7084042,28.009011 14.7028802,28.0131468 14.6973563,28.0172825 C14.6628319,28.0352041 14.6296885,28.05864 14.5937832,28.0793188 C14.5744495,28.0862117 14.5551158,28.0931046 14.5371632,28.1041333 C14.5040197,28.1248121 14.4764002,28.1523837 14.4460188,28.1771983 C14.3134451,28.2654276 14.1891573,28.3660643 14.0717743,28.4777296 C14.0634885,28.4860011 14.0524407,28.4915154 14.0441548,28.4997869 C14.0358689,28.5080584 14.030345,28.5190871 14.0220592,28.5273586 C13.8729138,28.6776242 13.7389592,28.8444329 13.6243382,29.0319203 C13.6160523,29.0443276 13.6050045,29.0553563 13.5980996,29.0677635 C13.5967187,29.0705207 13.5953377,29.0746564 13.5939567,29.0787922 C13.577385,29.1077425 13.5552894,29.1311784 13.5387177,29.1601286 L1.0864591,52.4251122 C0.368351699,53.7664744 0.873788832,55.4345611 2.21609729,56.1541819 L13.5262889,62.2144372 L13.5262889,81.7586225 L13.5262889,84.8108075 C13.5262889,85.8254453 13.9295338,86.7601252 14.8257871,87.2398725 L70.4445864,117.090352 C70.584065,117.164795 70.6931621,117.20891 70.8395455,117.25716 C70.9072133,117.280596 70.9500236,117.317818 71.0204533,117.33574 C71.2414095,117.390883 71.4568417,117.419833 71.6819407,117.419833 C71.9360403,117.419833 72.181854,117.382612 72.4276677,117.313682 C72.5077643,117.290246 72.5754321,117.244753 72.6541477,117.215803 C72.8019121,117.16066 72.9524385,117.113788 73.090536,117.029694 C73.1057268,117.021423 73.1140126,117.007637 73.1264414,117.000744 C73.1388702,116.992472 73.1526799,116.991094 73.1637277,116.982822 L126.106196,83.0269198 C126.121387,83.0172697 126.132435,83.0021053 126.146244,82.9938337 C126.320247,82.8780327 126.48044,82.7456886 126.624062,82.595423 C126.646158,82.5706085 126.661348,82.5430368 126.683444,82.5182223 C126.799446,82.3872568 126.900257,82.2466412 126.990021,82.0936184 C127.020402,82.0412322 127.048022,81.9860888 127.075641,81.9323241 C127.14469,81.7944657 127.199929,81.6524715 127.24412,81.5022059 C127.262073,81.4456839 126.977592,81.3932977 126.990021,81.3353972 C127.038355,81.1313668 126.766302,80.9232006 126.766302,80.7067629 L126.766302,44.1246579 L133.987425,47.5187317 C134.375479,47.7089763 134.944441,47.8013415 135.357353,47.8013415 C136.143128,47.8013415 136.991047,47.4663455 137.528247,46.841847 C138.349927,45.8878669 138.496311,44.5010114 137.778203,43.4698305 L137.778203,43.4698305 Z" fill="#000000"></path>
+                <path d="M121.242399,79.2013492 L74.289223,109.614291 L74.289223,60.7035091 L121.242399,34.1175167 L121.242399,79.2013492 L121.242399,79.2013492 Z" id="Path" fill="#FFFFFF"></path>
+                <path d="M19.050192,83.1606424 L19.050192,65.1756356 L57.9122313,86.0060398 C58.3182382,86.2238561 58.7670553,86.3341428 59.2186344,86.3341428 C59.4962105,86.3341428 59.7751676,86.2927853 60.0458389,86.207313 C60.7556604,85.985361 61.3453371,85.4876922 61.6822952,84.8245933 L68.7653199,70.8967592 L68.7653199,110.05268 L19.050192,83.1606424 L19.050192,83.1606424 Z" id="Path" fill="#FFFFFF"></path>
+                <path d="M17.1306357,34.1533599 L67.9671159,60.2871767 L58.0351381,79.8134405 L7.25942082,52.5960566 L17.1306357,34.1533599 L17.1306357,34.1533599 Z" id="Path" fill="#FFFFFF"></path>
+            </g>
+            <g id="Drop" transform="translate(43.000000, 0.000000)" sketch:type="MSShapeGroup">
+                <path d="M26.106,82.153 C19.598,82.153 13.731,79.739 8.667,74.979 C4.48,71.043 1.9,66.124 0.998,60.355 C0.182,55.139 0.904,50.091 3.144,45.351 C4.978,41.47 7.018,37.706 8.771,34.538 C10.073,32.182 11.439,29.827 12.76,27.549 C13.907,25.57 15.094,23.524 16.224,21.502 C17.63,18.988 19.129,14.294 20.333,10.522 C22.644,3.286 23.422,0.848 26.21,0.848 C27.099,0.848 27.951,1.244 28.548,1.933 L28.868,2.303 L29.045,2.759 C29.267,3.333 29.524,4.008 29.811,4.762 C31.737,9.814 35.323,19.218 38.354,24.468 L39.684,26.768 C41.863,30.534 44.115,34.427 46.253,38.317 C47.694,40.937 49.191,43.772 50.366,46.743 C53.153,53.786 52.968,60.693 49.815,67.272 C45.933,75.375 39.246,80.398 30.479,81.798 C29.005,82.034 27.533,82.153 26.106,82.153 L26.106,82.153 Z" id="Path" fill="#FFFFFF"></path>
+                <path d="M26.13,10.837 C25.975,11.317 25.825,11.788 25.684,12.231 C24.336,16.452 22.809,21.237 21.126,24.244 C19.975,26.303 18.777,28.369 17.618,30.367 C16.311,32.62 14.96,34.951 13.685,37.257 C11.974,40.35 9.987,44.015 8.221,47.753 C6.471,51.456 5.907,55.405 6.546,59.488 C7.251,63.993 9.258,67.829 12.513,70.887 C17.398,75.479 23.145,77.283 29.596,76.252 C36.559,75.14 41.659,71.302 44.753,64.845 C47.203,59.734 47.335,54.338 45.147,48.809 C44.086,46.128 42.688,43.482 41.335,41.023 C39.226,37.187 36.989,33.321 34.825,29.581 L33.492,27.276 C30.943,22.861 28.213,16.193 26.13,10.837 L26.13,10.837 Z" id="Path" fill="#3498DB"></path>
+                <path d="M29.403,69.008 C28.982,71.605 34.516,72.196 37.831,69.473 C41.618,66.361 43.768,60.528 41.662,59.667 C39.287,58.697 39.335,63.399 36.109,65.691 C33.17,67.781 29.767,66.762 29.403,69.008 L29.403,69.008 Z" id="Shape" fill="#FFFFFF"></path>
+            </g>
+        </g>
+    </g>
+</svg>
diff --git a/src/DrupalPackagist/Bundle/Resources/public/img/loader-white.gif b/src/DrupalPackagist/Bundle/Resources/public/img/loader-white.gif
new file mode 100644
index 0000000..de986a2
Binary files /dev/null and b/src/DrupalPackagist/Bundle/Resources/public/img/loader-white.gif differ
diff --git a/src/DrupalPackagist/Bundle/Resources/public/img/loader.gif b/src/DrupalPackagist/Bundle/Resources/public/img/loader.gif
new file mode 100644
index 0000000..7a7cf8e
Binary files /dev/null and b/src/DrupalPackagist/Bundle/Resources/public/img/loader.gif differ
diff --git a/src/DrupalPackagist/Bundle/Resources/public/img/logo-small.png b/src/DrupalPackagist/Bundle/Resources/public/img/logo-small.png
new file mode 100644
index 0000000..279464a
Binary files /dev/null and b/src/DrupalPackagist/Bundle/Resources/public/img/logo-small.png differ
diff --git a/src/DrupalPackagist/Bundle/Resources/public/img/logo.png b/src/DrupalPackagist/Bundle/Resources/public/img/logo.png
new file mode 100644
index 0000000..289548d
Binary files /dev/null and b/src/DrupalPackagist/Bundle/Resources/public/img/logo.png differ
diff --git a/src/DrupalPackagist/Bundle/Resources/public/js/charts.js b/src/DrupalPackagist/Bundle/Resources/public/js/charts.js
new file mode 100644
index 0000000..8d2a3b6
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/js/charts.js
@@ -0,0 +1,169 @@
+(function ($) {
+    "use strict";
+
+    var colors = [
+        '#0678be',
+        '#2d2d32'
+    ];
+
+    function initPackagistChart(svg, labels, series, withDatePicker) {
+        var format = d3.time.format("%Y-%m-%d");
+        if (labels[0].match(/^\d+-\d+$/)) {
+            format = d3.time.format("%Y-%m");
+        }
+
+        var chartData = [];
+        series.map(function (serie, index) {
+            var points = [];
+            labels.map(function (label, index) {
+                points.push({x: format.parse(label), y: parseInt(serie.values[index]) || 0});
+            })
+            chartData.push({
+                values: points,
+                key: serie.name,
+                color: colors[index]
+            });
+        })
+
+        if (withDatePicker && $(window).width() < 767) {
+            withDatePicker = false;
+        }
+
+        nv.addGraph(function() {
+            var chart;
+            if (withDatePicker) {
+                chart = nv.models.lineWithFocusChart();
+            } else {
+                chart = nv.models.lineChart();
+            }
+
+            function formatDate(a,b) {
+                if (!(a instanceof Date)) {
+                    a = new Date(a);
+                }
+                return format(a,b);
+            }
+            function formatDigit(a,b) {
+                if (a > 1000000) {
+                    return Math.round(a/1000000) + 'mio';
+                }
+                if (a > 1000) {
+                    return Math.round(a/1000) + 'K';
+                }
+                return a;
+            }
+
+            chart.xAxis.tickFormat(formatDate);
+            chart.yAxis.tickFormat(formatDigit);
+
+            if (withDatePicker) {
+                chart.x2Axis.tickFormat(formatDate);
+                chart.y2Axis.tickFormat(formatDigit);
+            }
+
+            d3.select(svg)
+                .datum(chartData)
+                .transition().duration(100)
+                .call(chart);
+
+            nv.utils.windowResize(chart.update);
+
+            return chart;
+        });
+    };
+
+    $('svg[data-labels]').each(function () {
+        initPackagistChart(
+            this,
+            $(this).attr('data-labels').split(','),
+            $(this).attr('data-values').split('|').map(function (values) {
+                values = values.split(':');
+                return {
+                    name: values[0],
+                    values: values[1].split(',')
+                };
+            })
+        );
+    });
+
+    window.initPackageStats = function (average, date, versions, statsUrl, versionStatsUrl) {
+        var match,
+            hash = document.location.hash,
+            versionCache = {},
+            ongoingRequest = false;
+
+        function initChart(type, res) {
+            initPackagistChart(
+                $('.js-'+type+'-dls')[0],
+                res.labels,
+                [{name: 'Daily Downloads', values: res.values}],
+                true
+            );
+        }
+
+        $.ajax({
+            url: statsUrl,
+            success: function (res) {
+                initChart('all', res);
+            }
+        })
+        function loadVersionChart(versionId) {
+            ongoingRequest = true;
+            $.ajax({
+                url: versionStatsUrl.replace('_VERSION_', versionId) + '?average=' + average + '&from=' + date,
+                success: function (res) {
+                    initChart('version', res);
+                    versionCache[versionId] = res;
+                    ongoingRequest = false;
+                }
+            });
+        }
+
+        // initializer for #<version-id> present on page load
+        if (hash.length > 1) {
+            hash = hash.substring(1);
+            match = $('.package .details-toggler[data-version-id="'+hash+'"]');
+            if (match.length) {
+                $('.package .details-toggler.open').removeClass('open');
+                match.addClass('open');
+            }
+        }
+
+        if ($('.package .details-toggler.open').length) {
+            loadVersionChart($('.package .details-toggler.open').attr('data-version-id'));
+        }
+
+        $('.package .details-toggler').on('click', function () {
+            var res, target = $(this), versionId = target.attr('data-version-id');
+
+            if (versionCache[versionId]) {
+                res = versionCache[versionId];
+                initChart('version', res);
+            } else {
+                if (ongoingRequest) {
+                    return;
+                }
+                loadVersionChart(versionId);
+            }
+
+            $('.package .details-toggler.open').removeClass('open');
+            target.addClass('open');
+        });
+
+        $(window).on('scroll', function () {
+            var basePos = $('.version-stats').offset().top;
+            var footerPadding = $(document).height() - basePos - $('footer').height() - $('.version-stats-chart').height() - 50;
+            var headerPadding = 80;
+            $('.version-stats-chart').css('top', Math.max(0, Math.min(footerPadding, window.scrollY - basePos + headerPadding)) + 'px');
+        });
+
+        // initialize version list expander
+        var versionsList = $('.package .versions')[0];
+        if (versionsList.offsetHeight < versionsList.scrollHeight) {
+            $('.package .versions-expander').removeClass('hidden').on('click', function () {
+                $(this).addClass('hidden');
+                $(versionsList).css('max-height', 'inherit');
+            });
+        }
+    };
+})(jQuery);
diff --git a/src/DrupalPackagist/Bundle/Resources/public/js/layout.js b/src/DrupalPackagist/Bundle/Resources/public/js/layout.js
new file mode 100644
index 0000000..d58191d
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/js/layout.js
@@ -0,0 +1,45 @@
+(function ($, humane) {
+    "use strict";
+
+    /**
+     * Ajax error handler
+     */
+    $.ajaxSetup({
+        error: function (xhr) {
+            var resp, message, details = '';
+
+            humane.remove();
+
+            if (xhr.responseText) {
+                try {
+                    resp = JSON.parse(xhr.responseText);
+                    if (resp.status && resp.status === 'error') {
+                        message = resp.message;
+                        details = resp.details;
+                    }
+                } catch (e) {
+                    message = "We're so sorry, something is wrong on our end.";
+                }
+            }
+
+            humane.log(details ? [message, details] : message, {timeout: 0, clickToClose: true});
+        }
+    });
+
+    /**
+     * API Token visibility toggling
+     */
+    var token = $('#api-token');
+    token.val('');
+
+    $('.btn-show-api-token,#api-token').each(function() {
+        $(this).click(function (e) {
+            token.val(token.data('api-token'));
+            token.select();
+
+            $('.btn-show-api-token').text('Your API token');
+
+            e.preventDefault();
+        });
+    });
+})(jQuery, humane);
\ No newline at end of file
diff --git a/src/DrupalPackagist/Bundle/Resources/public/js/search.js b/src/DrupalPackagist/Bundle/Resources/public/js/search.js
new file mode 100644
index 0000000..e6cfa8c
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/js/search.js
@@ -0,0 +1,163 @@
+/*jslint browser: true */
+/*global jQuery: true */
+(function ($) {
+    "use strict";
+
+    var list = $('.search-list'),
+        form = $('form#search-form'),
+        showResults,
+        doSearch,
+        searching = false,
+        searchQueued = false,
+        previousQuery = form.serialize(),
+        firstQuery = true;
+
+    showResults = function (page) {
+        var newList = $(page);
+
+        list.html(newList.html());
+        list.parent().removeClass('hidden');
+        list.find('ul.packages li:first').addClass('selected');
+        $('.order-by-group').attr('href', function (index, current) {
+            return current.replace(/q=.*?&/, 'q=' + encodeURIComponent($('input[type="search"]', form).val()) + '&')
+        });
+        $('.js-search-field-wrapper').removeClass('col-xs-12').addClass('col-xs-8');
+        $('#order-bys-wrapper').removeClass('hidden');
+
+        searching = false;
+
+        if (searchQueued) {
+            doSearch();
+            searchQueued = false;
+        }
+    };
+
+    doSearch = function () {
+        var currentQuery,
+            q,
+            pathname,
+            urlPrefix,
+            url,
+            title;
+
+        if (searching) {
+            searchQueued = true;
+            return;
+        }
+
+        if ($('#search_query_query').val().match(/^\s*$/) !== null) {
+            if (!firstQuery) {
+                list.parent().addClass('hidden');
+                $('#order-bys-wrapper').addClass('hidden');
+            }
+            return;
+        }
+
+        currentQuery = form.serialize();
+
+        if (previousQuery === currentQuery) {
+            return;
+        }
+
+        $('.order-by-group .active').removeClass('active');
+
+        if (window.history.pushState) {
+            q = encodeURIComponent($('input[type="search"]', form).val());
+
+            pathname = window.location.pathname;
+
+            if (pathname.indexOf('/app_dev.php') === 0) {
+                urlPrefix = '/app_dev.php';
+            } else if (pathname.indexOf('/app.php') === 0) {
+                urlPrefix = '/app.php';
+            } else {
+                urlPrefix = '';
+            }
+
+            url = urlPrefix + '/search/?q=' + q;
+            title = 'Search';
+
+            if (firstQuery) {
+                window.history.pushState(null, title, url);
+                firstQuery = false;
+            } else {
+                window.history.replaceState(null, title, url);
+            }
+        }
+
+        $.ajax({
+            url: form.attr('action'),
+            data: currentQuery,
+            success: showResults
+        });
+
+        searching = true;
+        previousQuery = currentQuery;
+    };
+
+    form.bind('keyup search', doSearch);
+
+    form.bind('keydown', function (event) {
+        var keymap,
+            currentSelected,
+            nextSelected;
+
+        keymap = {
+            enter: 13,
+            left: 37,
+            up: 38,
+            right: 39,
+            down: 40
+        };
+
+        if (keymap.up !== event.which && keymap.down !== event.which && keymap.enter !== event.which) {
+            return;
+        }
+
+        if ($('#search_query_query').val().match(/^\s*$/) !== null) {
+            document.activeElement.blur();
+            return;
+        }
+
+        event.preventDefault();
+
+        currentSelected = list.find('ul.packages li.selected');
+        nextSelected = (keymap.down === event.which) ? currentSelected.next('li') : currentSelected.prev('li');
+
+        if (currentSelected.length === 0 && keymap.down === event.which) {
+            currentSelected = list.find('ul.packages li:first').addClass('selected');
+        }
+
+        if (keymap.enter === event.which && currentSelected.data('url')) {
+            window.location = currentSelected.data('url');
+            return;
+        }
+
+        if (nextSelected.length > 0) {
+            currentSelected.removeClass('selected');
+            nextSelected.addClass('selected');
+
+            var elTop = nextSelected.position().top,
+                elHeight = nextSelected.height(),
+                windowTop = $(window).scrollTop(),
+                windowHeight = $(window).height();
+
+            if (elTop < windowTop) {
+                $(window).scrollTop(elTop);
+            } else if (elTop + elHeight > windowTop + windowHeight) {
+                $(window).scrollTop(elTop + elHeight + 20 - windowHeight);
+            }
+        }
+    });
+
+    // handle pressing S to focus the search
+    $(document.body).bind('keyup', function (event) {
+        if (event.which === 83 && (!document.activeElement || -1 === ['INPUT','SELECT','TEXTAREA'].indexOf(document.activeElement.tagName))) {
+            $('#search_query_query').focus();
+        }
+    });
+
+    if ($(document).width() >= 992) {
+        $('#search_query_query').focus();
+    }
+}(jQuery));
diff --git a/src/DrupalPackagist/Bundle/Resources/public/js/submitPackage.js b/src/DrupalPackagist/Bundle/Resources/public/js/submitPackage.js
new file mode 100644
index 0000000..d23877c
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/js/submitPackage.js
@@ -0,0 +1,52 @@
+(function ($) {
+    var showSimilarMax = 5;
+    var onSubmit = function(e) {
+        var success;
+        $('ul.package-errors, ul.similar-packages, div.confirmation', this).remove();
+        success = function (data) {
+            var html = '';
+            $('#submit').removeClass('loading');
+            if (data.status === 'error') {
+                $.each(data.reason, function (k, v) {
+                    html += '<li><div class="alert alert-warning">'+v+'</div></li>';
+                });
+                $('#submit-package-form').prepend('<ul class="list-unstyled package-errors">'+html+'</ul>');
+            } else {
+                if (data.similar.length) {
+                    var $similar = $('<ul class="list-unstyled similar-packages">');
+                    var limit = data.similar.length > showSimilarMax ? showSimilarMax : data.similar.length;
+                    for ( var i = 0; i < limit; i++ ) {
+                        var similar = data.similar[i];
+                        var $link = $('<a>').attr('href', similar.url).text(similar.name);
+                        $similar.append($('<li>').append($link))
+                    }
+                    if (limit != data.similar.length) {
+                        $similar.append($('<li>').text('And ' + (data.similar.length - limit) + ' more'));
+                    }
+                    $('#submit-package-form input[type="submit"]').before($('<div>').append(
+                        '<p><strong>Notice:</strong> One or more similarly named packages have already been submitted to Packagist. If this is a fork read the notice above regarding VCS Repositories.'
+                    ).append(
+                        '<p>Similarly named packages:'
+                    ).append($similar));
+                }
+                $('#submit-package-form input[type="submit"]').before(
+                    '<div class="confirmation">The package name found for your repository is: <strong>'+data.name+'</strong>, press Submit to confirm.</div>'
+                );
+                $('#submit').val('Submit');
+                $('#submit-package-form').unbind('submit');
+            }
+        };
+        $.post($(this).data('check-url'), $(this).serializeArray(), success);
+        $('#submit').addClass('loading');
+        e.preventDefault();
+    };
+
+    $('#package_repository').change(function() {
+        $('#submit-package-form').unbind('submit');
+        $('#submit-package-form').submit(onSubmit);
+        $('#submit').val('Check');
+    });
+
+    $('#package_repository').triggerHandler('change');
+})(jQuery);
+
diff --git a/src/DrupalPackagist/Bundle/Resources/public/js/view.js b/src/DrupalPackagist/Bundle/Resources/public/js/view.js
new file mode 100644
index 0000000..1304e1b
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/public/js/view.js
@@ -0,0 +1,181 @@
+/*jslint nomen: true, browser: true*/
+(function ($, humane) {
+    "use strict";
+
+    var versionCache = {},
+        ongoingRequest = false;
+
+    $('#add-maintainer').on('click', function (e) {
+        $('#remove-maintainer-form').addClass('hidden');
+        $('#add-maintainer-form').removeClass('hidden');
+        e.preventDefault();
+    });
+    $('#remove-maintainer').on('click', function (e) {
+        $('#add-maintainer-form').addClass('hidden');
+        $('#remove-maintainer-form').removeClass('hidden');
+        e.preventDefault();
+    });
+
+    $('.package .details-toggler').on('click', function () {
+        var target = $(this);
+
+        if (versionCache[target.attr('data-version-id')]) {
+            $('.package .version-details').html(versionCache[target.attr('data-version-id')]);
+        } else if (target.attr('data-load-more')) {
+            if (ongoingRequest) { // TODO cancel previous requests instead?
+                return;
+            }
+            ongoingRequest = true;
+            $('.package .version-details').addClass('loading');
+            $.ajax({
+                url: target.attr('data-load-more'),
+                dataType: 'json',
+                success: function (data) {
+                    versionCache[target.attr('data-version-id')] = data.content;
+                    ongoingRequest = false;
+                    $('.package .version-details')
+                        .removeClass('loading')
+                        .html(data.content);
+                }
+            });
+        }
+
+        $('.package .versions .open').removeClass('open');
+        target.addClass('open');
+    });
+
+    // initializer for #<version-id> present on page load
+    (function () {
+        var hash = document.location.hash;
+        if (hash.length > 1) {
+            hash = hash.substring(1);
+            $('.package .details-toggler[data-version-id="'+hash+'"]').click();
+        }
+    }());
+
+    function dispatchAjaxForm(form, success, className) {
+        var options = {
+            cache: false,
+            success: success,
+            data: $(form).serializeArray(),
+            type: $(form).attr('method'),
+            url: $(form).attr('action')
+        };
+        if ($(form).is('.' + className)) {
+            return;
+        }
+        $.ajax(options).complete(function () { $(form).removeClass(className); });
+        $(form).addClass(className);
+    }
+
+    function forceUpdatePackage(e, updateAll) {
+        var submit = $('input[type=submit]', '.package .force-update'), data;
+        if (e) {
+            e.preventDefault();
+        }
+        if (submit.is('.loading')) {
+            return;
+        }
+        data = $('.package .force-update').serializeArray();
+        if (updateAll) {
+            data.push({name: 'updateAll', value: '1'});
+        }
+        if (e && e.shiftKey) {
+            data.push({name: 'showOutput', value: '1'});
+        }
+        $.ajax({
+            url: $('.package .force-update').attr('action'),
+            dataType: 'json',
+            cache: false,
+            data: data,
+            type: $('.package .force-update').attr('method'),
+            success: function () {
+                document.location.reload(true);
+            },
+            context: $('.package .force-update')[0]
+        }).complete(function () { submit.removeClass('loading'); });
+        submit.addClass('loading');
+    }
+    $('.package .force-update').on('submit', forceUpdatePackage);
+    $('.package .force-update').on('click', forceUpdatePackage);
+    $('.package .mark-favorite').on('click', function (e) {
+        var options = {
+            dataType: 'json',
+            cache: false,
+            success: function () {
+                $(this).toggleClass('is-starred');
+            },
+            context: this
+        };
+        e.preventDefault();
+        if ($(this).is('.loading')) {
+            return;
+        }
+        if ($(this).is('.is-starred')) {
+            options.type = 'DELETE';
+            options.url = $(this).data('remove-url');
+        } else {
+            options.type = 'POST';
+            options.data = {"package": $(this).data('package')};
+            options.url = $(this).data('add-url');
+        }
+        $.ajax(options).complete(function () { $(this).removeClass('loading'); });
+        $(this).addClass('loading');
+    });
+    $('.package .delete').on('submit', function (e) {
+        e.preventDefault();
+        if (window.confirm('Are you sure?')) {
+            dispatchAjaxForm(this, function () {
+                humane.log('Package successfully deleted', {timeout: 0, clickToClose: true});
+                setTimeout(function () {
+                    document.location.href = document.location.href.replace(/\/[^\/]+$/, '/');
+                })
+            }, 'request-sent');
+        }
+    });
+    $('.package .delete-version .submit').on('click', function (e) {
+        e.preventDefault();
+        e.stopImmediatePropagation();
+        $(e.target).closest('form').submit();
+    });
+
+    $('.package .delete-version').on('submit', function (e) {
+        e.preventDefault();
+        e.stopImmediatePropagation();
+        var form = this;
+        if (window.confirm('Are you sure?')) {
+            dispatchAjaxForm(this, function () {
+                humane.log('Version successfully deleted', {timeout: 3000, clickToClose: true});
+                $(form).closest('.version').remove();
+            }, 'request-sent');
+        }
+    });
+    $('.package').on('click', '.requireme input', function () {
+        this.select();
+    });
+    if ($('.package').data('force-crawl')) {
+        forceUpdatePackage(null, true);
+    }
+
+    $('.readme a').on('click', function (e) {
+        var targetEl,
+            href = e.target.getAttribute('href');
+
+        if (href.substr(0, 1) === '#') {
+            targetEl = $(href);
+            if (targetEl.length) {
+                window.scrollTo(0, targetEl.offset().top - 70);
+                e.preventDefault();
+                e.stopImmediatePropagation();
+            }
+        }
+    });
+
+    var versionsList = $('.package .versions')[0];
+    if (versionsList.offsetHeight < versionsList.scrollHeight) {
+        $('.package .versions-expander').removeClass('hidden').on('click', function () {
+            $(this).addClass('hidden');
+            $(versionsList).css('max-height', 'inherit');
+        });
+    }
+}(jQuery, humane));
diff --git a/src/DrupalPackagist/Bundle/Resources/views/About/about.html.twig b/src/DrupalPackagist/Bundle/Resources/views/About/about.html.twig
new file mode 100644
index 0000000..f3c4461
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/views/About/about.html.twig
@@ -0,0 +1,35 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block content %}
+    <h2 class="title">What is Drupal Packagist?</h2>
+    <p>Drupal Packagist is a Composer package repository for Drupal. It is a
+        fork of <a href="https://packagist.org">Packagist</a> to automatically
+        provide packages for any projects hosted on drupal.org.
+    </p>
+
+    <p>To find out more about Composer please refer <a href="http://getcomposer.org/">to the official website</a>.</p>
+
+    <section class="row">
+        <div class="clearfix"></div>
+
+        <section class="col-md-6">
+            <h3 id="community">Contributing / Donating</h3>
+            <p>To report issues or contribute code you can find the source repository on <a href="https://github.com/drupal-composer/drupal-packagist">GitHub</a>.</p>
+        </section>
+
+        <section class="col-md-6">
+            <h3 id="community">Supporting Packagist & Composer</h3>
+            <p>Drupal Packagist is a fork of Packagist. If you would like to support our upstream project, the best way is to check out <a href="https://toranproxy.com">Toran Proxy</a> and buy a license. It can help you install packages fast and reliably, provides you with a private package repository, and the money goes towards Composer and Packagist maintenance!</p>
+        </section>
+    </section>
+
+    <h2 class="title" id="faq">FAQ</h2>
+    <section class="row">
+        <section class="col-md-12">
+            <h3>How to submit packages?</h3>
+            <p>Packages are automatically submitted. Drupal Packagist crawls full projects from Drupal.org at least once per day.</p>
+        </section>
+    </section>
+
+    <div class="clearfix"></div>
+{% endblock %}
diff --git a/src/DrupalPackagist/Bundle/Resources/views/Web/index.html.twig b/src/DrupalPackagist/Bundle/Resources/views/Web/index.html.twig
new file mode 100644
index 0000000..c785e00
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/views/Web/index.html.twig
@@ -0,0 +1,84 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block content %}
+<section class="row">
+    <section class="col-lg-6 getting-started">
+        <h2 class="title">Getting Started</h2>
+        <div>
+            <h3>Learn about Composer</h3>
+            <p>
+                For getting started with Composer in general, please visit the
+                <a href="https://getcomposer.org/doc/00-intro.md">offical documentation</a>.
+            </p>
+            <h3>Add Drupal Packagist</h3>
+            <p>
+                For being able to add any Drupal project (module, theme, drush
+                extension) to your composer project, you have to add the
+                Drupal Packagist to your root <em>composer.json</em>'s repository section:
+            </p>
+            <pre>
+    {
+      "repositories": [
+        {
+          "type": "composer",
+          "url": "https://packagist.drupal-composer.org"
+        }
+      ]
+    }
+            </pre>
+            <h3>Kickstart</h3>
+            <p>
+                We provide project templates for an easy start. Look at
+                <a href="https://github.com/drupal-composer/drupal-project">drupal-composer/drupal-project</a>
+                for details.
+            </p>
+            <p>
+                Visit <a href="http://drupal-composer.org/">drupal-composer.org</a>
+                for a general overview on the subject.
+            </p>
+        </div>
+    </section>
+
+    <section class="col-lg-6 publishing-packages">
+        <h2 class="title">Drupal 8 & Drupal 7</h2>
+        <div>
+            <p>
+                Basically Drupal Packagist will work for <em>Drupal 8</em> and
+                <em>Drupal 7</em> projects.
+            </p>
+            <h3>Composer installers</h3>
+            <p>
+                In general you will need to add a <a
+                    href="https://github.com/composer/installers">Composer
+                    Installer</a> to your project
+                so modules, themes, profiles and drush extensions are downloaded
+                to
+                the correct directories, dependent on your Drupal installation.
+            </p>
+            <h3>Adding modules, themes, …</h3>
+            <p>
+                Besides that, you can use <code>composer install</code>, <code>composer
+                    require</code>,
+                <code>composer update</code> and <code>composer remove</code>
+                like with
+                any other composer package.
+            </p>
+            <pre>
+   "require": [
+     {
+       "drupal/views": "^7.3.0",
+       "drupal/radix": "7.*",
+       "drupal/master": "7.3.*"
+     }
+   ]
+            </pre>
+            <h3>Documentation</h3>
+            <p>
+                <a href="https://www.drupal.org/node/2404989">Documentation on drupal.org</a> was started
+                but is far from complete. Any help is welcome to improve the
+                situation there.
+            </p>
+        </div>
+    </section>
+</section>
+{% endblock %}
diff --git a/src/DrupalPackagist/Bundle/Resources/views/Web/searchSection.html.twig b/src/DrupalPackagist/Bundle/Resources/views/Web/searchSection.html.twig
new file mode 100644
index 0000000..5e7caf7
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/views/Web/searchSection.html.twig
@@ -0,0 +1,18 @@
+<section class="wrapper wrapper-search">
+    <div class="container {% if showSearchDesc == 'show' %}with-description{% endif %}">
+        {{ render(controller('PackagistWebBundle:Web:searchForm', { req: app.request })) }}
+
+        {%- if showSearchDesc == 'show' %}
+        <div class="row">
+            <div class="col-sm-9 hidden-xs">
+                <img src="{{ asset('bundles/drupalpackagist/img/drupal-packagist.svg') }}" class="logo">
+                <p>Drupal Packagist is a <a href="http://getcomposer.org/">Composer</a>
+                    repository for <a href="https://www.drupal.org">Drupal</a>.
+                    It provides all projects from Drupal.org as packages for
+                    Composer.
+                </p>
+            </div>
+        </div>
+        {%- endif %}
+    </div>
+</section>
diff --git a/src/DrupalPackagist/Bundle/Resources/views/layout.html.twig b/src/DrupalPackagist/Bundle/Resources/views/layout.html.twig
new file mode 100644
index 0000000..8ea4ae5
--- /dev/null
+++ b/src/DrupalPackagist/Bundle/Resources/views/layout.html.twig
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8" />
+
+        <title>{% block title %}Drupal Packagist{% endblock %}</title>
+        <meta name="description" content="{% block description %}The PHP Package Repository{% endblock %}" />
+        <meta name="author" content="Jordi Boggiano" />
+
+        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
+        <link rel="shortcut icon" href="{{ asset('favicon.ico') }}" />
+
+        <link rel="stylesheet" href="{{ asset('libs/bootstrap-3.3.5/css/bootstrap.min.css') }}" />
+        <link rel="stylesheet" href="{{ asset('libs/bootstrap-3.3.5/css/bootstrap-theme.min.css') }}" />
+        <link rel="stylesheet" href="{{ asset('bundles/drupalpackagist/css/main.css') }}" />
+        <link rel="stylesheet" href="{{ asset('css/humane/jackedup.css') }}" />
+        {% block stylesheets %}{% endblock %}
+
+        {% block head_feeds %}
+        <link rel="alternate" type="application/rss+xml" title="Newly Submitted Packages - Packagist" href="{{ url('feed_packages', {_format: 'rss'}) }}" />
+        <link rel="alternate" type="application/rss+xml" title="New Releases - Packagist" href="{{ url('feed_releases', {_format: 'rss'}) }}" />
+        {% endblock %}
+
+        <link rel="search" type="application/opensearchdescription+xml" href="{{ asset('search.osd') }}" title="Packagist" />
+
+        {% block head_additions %}{% endblock %}
+    </head>
+
+    <body>
+    <section class="wrap">
+        <header class="navbar-wrapper navbar-fixed-top">
+            <nav class="container">
+                <div class="navbar" role="navigation">
+                    <div class="navbar-header">
+                        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
+                            <span class="sr-only">Toggle navigation</span>
+                            <span class="icon-bar"></span>
+                            <span class="icon-bar"></span>
+                            <span class="icon-bar"></span>
+                        </button>
+                        <h1 class="navbar-brand"><a href="{{ path('home') }}">Drupal Packagist</a> <em class="hidden-sm hidden-xs">The (deprecated) Package Repository for Drupal</em></h1>
+                    </div>
+
+                    <div class="collapse navbar-collapse">
+                        <ul class="nav navbar-nav">
+                            <li>
+                                <a href="{{ path('browse') }}">Browse</a>
+                            </li>
+                        {%- if app.user %}
+                            <li class="nav-user">
+                                <section>
+                                    <a href="{{ path('fos_user_profile_show') }}">{{ app.user.username }}</a>
+
+                                    <section class="nav-user-menu">
+                                        {{ knp_menu_render('user_menu', {'allow_safe_labels': true}) }}
+                                    </section>
+                                </section>
+                            </li>
+                        {%- endif %}
+                        </ul>
+                    </div>
+                </div>
+            </nav>
+        </header>
+
+        <div class="deprecation-notice">
+             <div class="container">
+                 <p>Drupal Packagist is scheduled to be deprecated in January 2017 (or when the <a href="https://www.drupal.org/node/2718229">official Package Repository from Drupal.org</a> is ready). Until then Drupal Packagist will continue to receive updates for all modules and themes.</p>
+             </div>
+        </div>
+
+        {{ include("PackagistWebBundle:Web:searchSection.html.twig", {showSearchDesc: showSearchDesc|default('show')}) }}
+
+        {% block flashMessages %}
+            {%- if app.session.flashbag.peekAll()|length > 0 %}
+            <section class="wrapper wrapper-white">
+                <div class="container flash-container">
+                {%- for type, flashMessages in app.session.flashbag.all() %}
+                    {%- for flashMessage in flashMessages %}
+                        {%- if 'fos_user_' in type %}
+                            <div class="alert alert-{{ type|replace({'fos_user_': ''}) }}">
+                                <p>{{ flashMessage|trans({}, 'FOSUserBundle') }}</p>
+                            </div>
+                        {%- else %}
+                            <div class="alert alert-{{ type }}">
+                                <p>{{ flashMessage|trans }}</p>
+                            </div>
+                        {%- endif %}
+                    {%- endfor %}
+                {%- endfor %}
+                </div>
+            </section>
+            {%- endif %}
+        {% endblock %}
+
+        {% block content_header %}{% endblock %}
+
+        <section class="wrapper">
+            <section class="container content" role="main">
+                {% block search_results %}
+                    <div class="row hidden">
+                        <div class="search-list col-md-12"></div>
+                    </div>
+                {% endblock %}
+
+                {% block content %}{% endblock %}
+            </section>
+        </section>
+    </section>
+
+        <footer class="wrapper-footer">
+            <nav class="container">
+                <div class="row">
+                    <ul class="social col-xs-7 col-sm-4 col-md-2 pull-right">
+                        <li><a href="http://github.com/drupal-composer/drupal-packagist" title="{{ 'menu.github'|trans }}"><span class="icon-github"></span></a></li>
+                        <li><a href="https://twitter.com/drupal_composer" title="{{ 'menu.twitter'|trans }}"><span class="icon-twitter"></span></a></li>
+                        <li><a href="mailto:info@drupal-composer.org" title="{{ 'menu.contact'|trans }}"><span class="icon-mail"></span></a></li>
+                    </ul>
+
+                    <ul class="col-xs-4 col-md-2">
+                        <li><a href="{{ path('about') }}">{{ 'menu.about_packagist'|trans }}</a></li>
+                        <li><a href="{{ path('feeds') }}">{{ 'menu.rss_feeds'|trans }}</a></li>
+                    </ul>
+
+                    <div class="clearfix visible-xs-block"></div>
+
+                    <ul class="col-xs-3 col-md-2">
+                        <li><a href="{{ path('stats') }}">{{ 'menu.stats'|trans }}</a></li>
+                        <li><a href="{{ path('browse') }}">{{ 'menu.browse_packages'|trans }}</a></li>
+                    </ul>
+
+                    <div class="clearfix visible-xs-block"></div>
+
+                    <ul class="col-xs-3 col-md-2">
+                        <li><a href="{{ path('api_doc') }}">{{ 'menu.api_doc'|trans }}</a></li>
+                    </ul>
+
+                </div>
+
+                <div class="row">
+                    <p class="col-xs-12">Drupal Packagist is based on <a href="https://github.com/composer/packagist">Packagist</a>. Drupal Packagist is currently maintained by <a href="https://twitter.com/webflo">@webflo</a>.</p>
+                </div>
+            </nav>
+        </footer>
+
+        <script src="{{ asset('libs/jquery-2.1.4.min.js') }}"></script>
+        <script src="{{ asset('libs/humane-3.2.2.min.js') }}"></script>
+        <script src="{{ asset('bundles/drupalpackagist/js/layout.js') }}"></script>
+        <script src="{{ asset('bundles/drupalpackagist/js/search.js')}}"></script>
+        <script src="{{ asset('libs/bootstrap-3.3.5/js/bootstrap.min.js')}}"></script>
+
+        {%- if not app.debug and google_analytics.ga_key %}
+        <script>
+            var _gaq=[['_setAccount','{{ google_analytics.ga_key }}'],['_trackPageview']];
+            (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.async=1;
+            g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
+            s.parentNode.insertBefore(g,s)}(document,'script'));
+        </script>
+        {%- endif %}
+
+        {% block scripts %}{% endblock %}
+    </body>
+</html>
diff --git a/src/Packagist/Redis/DownloadsIncr.php b/src/Packagist/Redis/DownloadsIncr.php
new file mode 100644
index 0000000..2ef4c54
--- /dev/null
+++ b/src/Packagist/Redis/DownloadsIncr.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Packagist\Redis;
+
+class DownloadsIncr extends \Predis\Command\ScriptCommand
+{
+    public function getKeysCount()
+    {
+        return 7;
+    }
+
+    public function getScript()
+    {
+        return <<<LUA
+redis.call("incr", KEYS[1]);
+redis.call("incr", KEYS[2]);
+redis.call("incr", KEYS[3]);
+redis.call("incr", KEYS[4]);
+redis.call("incr", KEYS[5]);
+redis.call("incr", KEYS[6]);
+redis.call("incr", KEYS[7]);
+LUA;
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/AddPackagesCommand.php b/src/Packagist/WebBundle/Command/AddPackagesCommand.php
new file mode 100644
index 0000000..3d09006
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/AddPackagesCommand.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Packagist\WebBundle\Entity\Package;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+
+class AddPackagesCommand extends ContainerAwareCommand
+{
+  protected function configure()
+  {
+      $this
+          ->setName('packagist:add')
+          ->setDefinition(array(
+              new InputOption(
+                  'force',
+                  null,
+                  InputOption::VALUE_NONE,
+                  'Overwrite existing packages'
+              ),
+              new InputOption(
+                  'vendor',
+                  null,
+                  InputOption::VALUE_OPTIONAL,
+                  'default vendor name'
+              ),
+              new InputOption(
+                  'repo-pattern',
+                  null,
+                  InputOption::VALUE_OPTIONAL,
+                  'pattern for repo url',
+                  'https://github.com/%s'
+              ),
+              new InputArgument(
+                  'packages',
+                  InputArgument::REQUIRED|InputArgument::IS_ARRAY,
+                  'list of packages to add'
+              )
+          ))->setDescription('Imports packages from packages.json');
+  }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = $input->getOption('verbose');
+        $force = $input->getOption('force');
+        $packages = $input->getArgument('packages');
+        $packages = is_array($packages) ? $packages : array($packages);
+
+        $doctrine = $this->getContainer()->get('doctrine');
+
+        $io = $verbose
+            ? new ConsoleIO(
+                $input,
+                $output,
+                $this->getApplication()->getHelperSet()
+            )
+            : new BufferIO('');
+
+        $em = $doctrine->getManager();
+        $flushed = $packagistPackages = array();
+        foreach ($packages as $key => $name) {
+            $fullName = $name;
+            if ($vendor = $input->getOption('vendor')) {
+                $fullName = "$vendor/$name";
+            }
+            if (!isset($packagistPackages[$fullName])) {
+                $package = new Package();
+                $io->write('downloading '.$fullName);
+                $package->setRepository(
+                    sprintf(
+                        $input->getOption('repo-pattern'),
+                        $fullName,
+                        $name,
+                        $vendor
+                    )
+                );
+                $package->setName($fullName);
+                $packagistPackages[$fullName] = true;
+                $io->write('saving '.$fullName);
+                $em->persist($package);
+                if ((count($packagistPackages) - count($flushed)) >= 100) {
+                    $em->flush();
+                    $flushed = $packagistPackages;
+                }
+            }
+        }
+        $em->flush();
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/BackgroundAddPackagesCommand.php b/src/Packagist/WebBundle/Command/BackgroundAddPackagesCommand.php
new file mode 100644
index 0000000..f7a2c29
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/BackgroundAddPackagesCommand.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Packagist\WebBundle\Package\Updater;
+use Packagist\WebBundle\Entity\Package;
+use Composer\Repository\VcsRepository;
+use Composer\Factory;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Composer\Repository\InvalidRepositoryException;
+use Composer\Repository\ComposerRepository;
+
+class BackgroundAddPackagesCommand extends ContainerAwareCommand
+{
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:bg_add')
+            ->setDefinition(array(
+                new InputOption(
+                    'force',
+                    null,
+                    InputOption::VALUE_NONE,
+                    'Overwrite existing packages'
+                ),
+                new InputOption(
+                    'vendor',
+                    null,
+                    InputOption::VALUE_OPTIONAL,
+                    'default vendor name'
+                ),
+                new InputOption(
+                    'repo-pattern',
+                    null,
+                    InputOption::VALUE_OPTIONAL,
+                    'pattern for repo url',
+                    'https://github.com/%s'
+                ),
+                new InputArgument(
+                    'packages',
+                    InputArgument::REQUIRED|InputArgument::IS_ARRAY,
+                    'list of packages to add'
+                )
+            ))->setDescription('Imports packages from packages.json');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = $input->getOption('verbose');
+        $force = $input->getOption('force');
+        $packages = $input->getArgument('packages');
+        $packages = is_array($packages) ? $packages : array($packages);
+        $io = $verbose
+            ? new ConsoleIO(
+                $input,
+                $output,
+                $this->getApplication()->getHelperSet()
+            )
+            : new BufferIO('');
+        $client = $this->getContainer()
+            ->get('old_sound_rabbit_mq.add_packages_rpc');
+        foreach ($packages as $key => $name) {
+            $fullName = $name;
+            if ($vendor = $input->getOption('vendor')) {
+                $fullName = "$vendor/$name";
+            }
+            $io->write('queuing '.$fullName);
+            $client->addRequest(
+                serialize(
+                    array(
+                        'url' => sprintf(
+                            $input->getOption('repo-pattern'),
+                            $fullName,
+                            $name,
+                            $vendor
+                        ),
+                        'package_name' => $name
+                    )
+                ),
+                'add_packages',
+                $name
+            );
+        }
+        $io->write('waiting...');
+        foreach ($client->getReplies() as $result) {
+            $output->write(unserialize($result)['output']);
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/BackgroundUpdatePackagesCommand.php b/src/Packagist/WebBundle/Command/BackgroundUpdatePackagesCommand.php
new file mode 100644
index 0000000..c51f3dc
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/BackgroundUpdatePackagesCommand.php
@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Packagist\WebBundle\Package\Updater;
+use Composer\Repository\VcsRepository;
+use Composer\Factory;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Composer\Repository\InvalidRepositoryException;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class BackgroundUpdatePackagesCommand extends ContainerAwareCommand
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:bg_update')
+            ->setDefinition(array(
+                new InputOption('force', null, InputOption::VALUE_NONE, 'Force a re-crawl of all packages'),
+                new InputOption('delete-before', null, InputOption::VALUE_NONE, 'Force deletion of all versions before an update'),
+                new InputOption('notify-failures', null, InputOption::VALUE_NONE, 'Notify failures to maintainers by email'),
+                new InputArgument('package', InputArgument::OPTIONAL, 'Package name to update'),
+            ))
+            ->setDescription('Updates packages')
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose  = $input->getOption('verbose');
+        $force    = $input->getOption('force');
+        $package  = $input->getArgument('package');
+        $doctrine = $this->getContainer()->get('doctrine');
+        $flags    = 0;
+        if ($package) {
+            $packages = array(array('name' => $package));
+            $flags = Updater::UPDATE_EQUAL_REFS;
+        }
+        elseif ($force) {
+            $packages = $doctrine->getManager()
+                ->getConnection()
+                ->fetchAll('SELECT name FROM package ORDER BY name ASC');
+            $flags = Updater::UPDATE_EQUAL_REFS;
+        }
+        else {
+            $packages = $doctrine->getRepository('PackagistWebBundle:Package')
+                ->getStalePackages();
+        }
+        $names = array();
+        foreach ($packages as $package) {
+            $names[] = $package['name'];
+        }
+        if ($input->getOption('delete-before')) {
+            $flags = Updater::DELETE_BEFORE;
+        }
+        $client = $this->getContainer()
+            ->get('old_sound_rabbit_mq.update_packages_rpc');
+        $input->setInteractive(false);
+        foreach ($names as $name) {
+            $output->write('Queuing job '.$name, true);
+            $client->addRequest(
+                serialize(
+                    array(
+                        'flags' => $flags,
+                        'package_name' => $name
+                    )
+                ),
+                'update_packages',
+                $name
+            );
+        }
+        foreach ($client->getReplies() as $result) {
+            $output->write(unserialize($result)['output']);
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/BulkAddPackagesCommand.php b/src/Packagist/WebBundle/Command/BulkAddPackagesCommand.php
new file mode 100644
index 0000000..797d263
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/BulkAddPackagesCommand.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Packagist\WebBundle\Entity\Package;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+
+class BulkAddPackagesCommand extends ContainerAwareCommand
+{
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:bulk_add')
+            ->setDefinition(array(
+                new InputOption(
+                    'force',
+                    null,
+                    InputOption::VALUE_NONE,
+                    'Overwrite existing packages'
+                ),
+                new InputOption(
+                    'vendor',
+                    null,
+                    InputOption::VALUE_OPTIONAL,
+                    'default vendor name'
+                ),
+                new InputOption(
+                    'repo-pattern',
+                    null,
+                    InputOption::VALUE_OPTIONAL,
+                    'pattern for repo url',
+                    'https://github.com/%s'
+                ),
+                new InputArgument(
+                    'packages',
+                    InputArgument::REQUIRED|InputArgument::IS_ARRAY,
+                    'list of packages to add'
+                )
+            ))->setDescription('Imports packages from packages.json');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = $input->getOption('verbose');
+        $packageArgs = $input->getArgument('packages');
+        $packages = [];
+        foreach ($packageArgs as $key => $packageArg) {
+            if (file_exists($packageArg)) {
+                $packageArg = preg_split(
+                    '/\s+/',
+                    file_get_contents($packageArg)
+                );
+            }
+            else {
+                $packageArg = [$packageArg];
+            }
+            $packages = array_merge($packages, $packageArg);
+        }
+        $io = $verbose
+            ? new ConsoleIO(
+                $input,
+                $output,
+                $this->getApplication()->getHelperSet()
+            )
+            : new BufferIO('');
+        $producer = $this->getContainer()
+            ->get('old_sound_rabbit_mq.add_packages_producer');
+        foreach ($packages as $name) {
+            $fullName = $name;
+            if ($vendor = $input->getOption('vendor')) {
+                $fullName = "$vendor/$name";
+            }
+            $io->write('queuing '.$fullName);
+            $producer->publish(
+                serialize(
+                    array(
+                        'url' => sprintf(
+                            $input->getOption('repo-pattern'),
+                            $fullName,
+                            $name,
+                            $vendor
+                        ),
+                        'package_name' => $fullName
+                    )
+                )
+            );
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/ClearVersionsCommand.php b/src/Packagist/WebBundle/Command/ClearVersionsCommand.php
new file mode 100644
index 0000000..6de6075
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/ClearVersionsCommand.php
@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ClearVersionsCommand extends ContainerAwareCommand
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:clear:versions')
+            ->setDefinition(array(
+                new InputOption('force', null, InputOption::VALUE_NONE, 'Force execution, by default it runs in dry-run mode'),
+                new InputOption('filter', null, InputOption::VALUE_NONE, 'Filter (regex) against "<version name> <version number>"'),
+            ))
+            ->setDescription('Clears all versions from the databases')
+            ->setHelp(<<<EOF
+
+EOF
+            )
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $force = $input->getOption('force');
+        $filter = $input->getOption('filter');
+        $doctrine = $this->getContainer()->get('doctrine');
+
+        $versionRepo = $doctrine->getRepository('PackagistWebBundle:Version');
+
+        $packages = $doctrine->getManager()->getConnection()->fetchAll('SELECT id FROM package ORDER BY id ASC');
+        $ids = array();
+        foreach ($packages as $package) {
+            $ids[] = $package['id'];
+        }
+
+        $packageNames = array();
+
+        while ($ids) {
+            $qb = $versionRepo->createQueryBuilder('v');
+            $qb
+                ->join('v.package', 'p')
+                ->where($qb->expr()->in('p.id', array_splice($ids, 0, 50)));
+            $versions = $qb->getQuery()->iterate();
+
+            foreach ($versions as $version) {
+                $version = $version[0];
+                $name = $version->getName().' '.$version->getVersion();
+                if (!$filter || preg_match('{'.$filter.'}i', $name)) {
+                    $output->writeln('Clearing '.$name);
+                    if ($force) {
+                        $packageNames[] = $version->getName();
+                        $versionRepo->remove($version);
+                    }
+                }
+            }
+
+            $doctrine->getManager()->flush();
+            $doctrine->getManager()->clear();
+            unset($versions);
+        }
+
+        if ($force) {
+            // mark packages as recently crawled so that they get updated
+            $packageRepo = $doctrine->getRepository('PackagistWebBundle:Package');
+            foreach ($packageNames as $name) {
+                $package = $packageRepo->findOneByName($name);
+                $package->setCrawledAt(new \DateTime);
+            }
+
+            $doctrine->getManager()->flush();
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/CompileStatsCommand.php b/src/Packagist/WebBundle/Command/CompileStatsCommand.php
new file mode 100644
index 0000000..397c8ea
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/CompileStatsCommand.php
@@ -0,0 +1,178 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Command;
+
+use Packagist\WebBundle\Package\Updater;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class CompileStatsCommand extends ContainerAwareCommand
+{
+    protected $redis;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:stats:compile')
+            ->setDefinition(array(
+                new InputOption('force', null, InputOption::VALUE_NONE, 'Force a re-build of all stats'),
+            ))
+            ->setDescription('Updates the redis stats indices')
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = $input->getOption('verbose');
+        $force = $input->getOption('force');
+
+        $doctrine = $this->getContainer()->get('doctrine');
+        $this->redis = $redis = $this->getContainer()->get('snc_redis.default');
+
+        $minMax = $doctrine->getManager()->getConnection()->fetchAssoc('SELECT MAX(id) maxId, MIN(id) minId FROM package');
+        if (!isset($minMax['minId'])) {
+            return 0;
+        }
+
+        $ids = range($minMax['minId'], $minMax['maxId']);
+        $res = $doctrine->getManager()->getConnection()->fetchAssoc('SELECT MIN(createdAt) minDate FROM package');
+        $date = new \DateTime($res['minDate']);
+        $date->modify('00:00:00');
+        $yesterday = new \DateTime('yesterday 00:00:00');
+
+        if ($force) {
+            if ($verbose) {
+                $output->writeln('Clearing aggregated DB');
+            }
+            $clearDate = clone $date;
+            $keys = array();
+            while ($clearDate <= $yesterday) {
+                $keys['downloads:'.$clearDate->format('Ymd')] = true;
+                $keys['downloads:'.$clearDate->format('Ym')] = true;
+                $clearDate->modify('+1day');
+            }
+            $redis->del(array_keys($keys));
+        }
+
+        while ($date <= $yesterday) {
+            // skip months already computed
+            if (null !== $this->getMonthly($date) && $date->format('m') !== $yesterday->format('m')) {
+                $date->setDate($date->format('Y'), $date->format('m')+1, 1);
+                continue;
+            }
+
+            // skip days already computed
+            if (null !== $this->getDaily($date) && $date != $yesterday) {
+                $date->modify('+1day');
+                continue;
+            }
+
+            $sum = $this->sum($date->format('Ymd'), $ids);
+            $redis->set('downloads:'.$date->format('Ymd'), $sum);
+
+            if ($verbose) {
+                $output->writeln('Wrote daily data for '.$date->format('Y-m-d').': '.$sum);
+            }
+
+            $nextDay = clone $date;
+            $nextDay->modify('+1day');
+            // update the monthly total if we just computed the last day of the month or the last known day
+            if ($date->format('Ymd') === $yesterday->format('Ymd') || $date->format('Ym') !== $nextDay->format('Ym')) {
+                $sum = $this->sum($date->format('Ym'), $ids);
+                $redis->set('downloads:'.$date->format('Ym'), $sum);
+
+                if ($verbose) {
+                    $output->writeln('Wrote monthly data for '.$date->format('Y-m').': '.$sum);
+                }
+            }
+
+            $date = $nextDay;
+        }
+
+        // fetch existing ids
+        $doctrine = $this->getContainer()->get('doctrine');
+        $packages = $doctrine->getManager()->getConnection()->fetchAll('SELECT id FROM package ORDER BY id ASC');
+        $ids = array();
+        foreach ($packages as $row) {
+            $ids[] = $row['id'];
+        }
+
+        // add downloads from the last 7 days to the solr index
+        $solarium = $this->getContainer()->get('solarium.client');
+
+        if ($verbose) {
+            $output->writeln('Writing new trendiness data into redis');
+        }
+
+        while ($id = array_shift($ids)) {
+            $trendiness = $this->sumLastNDays(7, $id, $yesterday);
+
+            $redis->zadd('downloads:trending:new', $trendiness, $id);
+            $redis->zadd('downloads:absolute:new', $redis->get('dl:'.$id), $id);
+        }
+
+        $redis->rename('downloads:trending:new', 'downloads:trending');
+        $redis->rename('downloads:absolute:new', 'downloads:absolute');
+    }
+
+    // TODO could probably run faster with lua scripting
+    protected function sumLastNDays($days, $id, \DateTime $yesterday)
+    {
+        $date = clone $yesterday;
+        $keys = array();
+        for ($i = 0; $i < $days; $i++) {
+            $keys[] = 'dl:'.$id.':'.$date->format('Ymd');
+            $date->modify('-1day');
+        }
+
+        return array_sum($this->redis->mget($keys));
+    }
+
+    protected function sum($date, array $ids)
+    {
+        $sum = 0;
+
+        while ($ids) {
+            $batch = array_splice($ids, 0, 500);
+            $keys = array();
+            foreach ($batch as $id) {
+                $keys[] = 'dl:'.$id.':'.$date;
+            }
+            $sum += array_sum($res = $this->redis->mget($keys));
+        }
+
+        return $sum;
+    }
+
+    protected function getMonthly(\DateTime $date)
+    {
+        return $this->redis->get('downloads:'.$date->format('Ym'));
+    }
+
+    protected function getDaily(\DateTime $date)
+    {
+        return $this->redis->get('downloads:'.$date->format('Ymd'));
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/DumpPackagesCommand.php b/src/Packagist/WebBundle/Command/DumpPackagesCommand.php
new file mode 100644
index 0000000..5749ea7
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/DumpPackagesCommand.php
@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Filesystem\LockHandler;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class DumpPackagesCommand extends ContainerAwareCommand
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:dump')
+            ->setDefinition(array(
+                new InputOption('force', null, InputOption::VALUE_NONE, 'Force a dump of all packages'),
+            ))
+            ->setDescription('Dumps the packages into a packages.json + included files')
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $force = (Boolean) $input->getOption('force');
+        $verbose = (Boolean) $input->getOption('verbose');
+
+        $deployLock = $this->getContainer()->getParameter('kernel.cache_dir').'/deploy.globallock';
+        if (file_exists($deployLock)) {
+            if ($verbose) {
+                $output->writeln('Aborting, '.$deployLock.' file present');
+            }
+            return;
+        }
+
+        $doctrine = $this->getContainer()->get('doctrine');
+
+        if ($force) {
+            $packages = $doctrine->getManager()->getConnection()->fetchAll('SELECT id FROM package ORDER BY id ASC');
+        } else {
+            $packages = $doctrine->getRepository('PackagistWebBundle:Package')->getStalePackagesForDumping();
+        }
+
+        $ids = array();
+        foreach ($packages as $package) {
+            $ids[] = $package['id'];
+        }
+        if (!$ids && !$force) {
+            if ($verbose) {
+                $output->writeln('Aborting, no packages to dump and not doing a forced run');
+            }
+            return 0;
+        }
+
+        $lock = new LockHandler('packagist_package_dumper');
+
+        ini_set('memory_limit', -1);
+        gc_enable();
+
+        // another dumper is still active
+        if (!$lock->lock()) {
+            if ($verbose) {
+                $output->writeln('Aborting, another dumper is still active');
+            }
+            return;
+        }
+
+        $result = $this->getContainer()->get('packagist.package_dumper')->dump($ids, $force, $verbose);
+        $lock->release();
+
+        return $result ? 0 : 1;
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/GenerateTokensCommand.php b/src/Packagist/WebBundle/Command/GenerateTokensCommand.php
new file mode 100644
index 0000000..c0bc8b0
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/GenerateTokensCommand.php
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class GenerateTokensCommand extends ContainerAwareCommand
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:tokens:generate')
+            ->setDescription('Generates all missing user tokens')
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $doctrine = $this->getContainer()->get('doctrine');
+        $userRepo = $doctrine->getRepository('PackagistWebBundle:User');
+        $tokenGenerator = $this->getContainer()->get('fos_user.util.token_generator');
+
+        $users = $userRepo->findUsersMissingApiToken();
+        foreach ($users as $user) {
+            $apiToken = substr($tokenGenerator->generateToken(), 0, 20);
+            $user->setApiToken($apiToken);
+        }
+        $doctrine->getManager()->flush();
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/ImportPackagesCommand.php b/src/Packagist/WebBundle/Command/ImportPackagesCommand.php
new file mode 100644
index 0000000..a8e4a70
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/ImportPackagesCommand.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Packagist\WebBundle\Package\Updater;
+use Packagist\WebBundle\Entity\Package;
+use Composer\Repository\VcsRepository;
+use Composer\Factory;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Composer\Repository\InvalidRepositoryException;
+use Composer\Repository\ComposerRepository;
+
+class ImportPackagesCommand extends ContainerAwareCommand
+{
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:import')
+            ->setDefinition(array(
+              new InputOption(
+              'force',
+              null,
+              InputOption::VALUE_NONE,
+              'Overwrite existing packages'
+            ),
+            new InputArgument(
+              'packages.json',
+              InputArgument::REQUIRED,
+              'Path to packages.json to import'
+            )
+          ))
+            ->setDescription('Imports packages from packages.json');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = $input->getOption('verbose');
+        $force = $input->getOption('force');
+        $packagesJson = $input->getArgument('packages.json');
+
+        $doctrine = $this->getContainer()->get('doctrine');
+        $router = $this->getContainer()->get('router');
+
+        $io = $verbose
+            ? new ConsoleIO(
+                $input,
+                $output,
+                $this->getApplication()->getHelperSet()
+            )
+            : new BufferIO('');
+
+        $repository = new ComposerRepository(
+            array(
+                'url' => $packagesJson
+            ),
+            $io,
+            Factory::createConfig()
+        );
+
+
+        $composerPackages = function ($repository) {
+            if ($repository->hasProviders()) {
+                foreach($repository->getProviderNames() as $packageName) {
+                    foreach($repository->findPackages($packageName) as $package) {
+                        yield $package;
+                    }
+                }
+            }
+            else {
+                foreach($repository->getPackages() as $package) {
+                    yield $package;
+                }
+            }
+        };
+        $packagistPackages = array();
+        $em = $doctrine->getManager();
+        foreach ($composerPackages($repository) as $composerPackage) {
+            // @todo move this into drupal/parse-composer
+            $name = strtolower($composerPackage->getPrettyName());
+            if (!isset($packagistPackages[$name])) {
+                $io->write("Importing $name");
+                $package = new Package();
+                $package->setRepository($composerPackage->getSourceUrl());
+                $package->setName($name);
+                $packagistPackages[$name] = $package;
+                $io->write("Importing $name");
+                $em->persist($package);
+            }
+        }
+        $em->flush();
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/IndexPackagesCommand.php b/src/Packagist/WebBundle/Command/IndexPackagesCommand.php
new file mode 100644
index 0000000..e293e4a
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/IndexPackagesCommand.php
@@ -0,0 +1,247 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Command;
+
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Model\DownloadManager;
+use Packagist\WebBundle\Model\FavoriteManager;
+use Solarium_Document_ReadWrite;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Filesystem\LockHandler;
+use Doctrine\DBAL\Connection;
+
+/**
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+class IndexPackagesCommand extends ContainerAwareCommand
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:index')
+            ->setDefinition(array(
+                new InputOption('force', null, InputOption::VALUE_NONE, 'Force a re-indexing of all packages'),
+                new InputOption('all', null, InputOption::VALUE_NONE, 'Index all packages without clearing the index first'),
+                new InputArgument('package', InputArgument::OPTIONAL, 'Package name to index'),
+            ))
+            ->setDescription('Indexes packages in Solr')
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = $input->getOption('verbose');
+        $force = $input->getOption('force');
+        $indexAll = $input->getOption('all');
+        $package = $input->getArgument('package');
+
+        $deployLock = $this->getContainer()->getParameter('kernel.cache_dir').'/deploy.globallock';
+        if (file_exists($deployLock)) {
+            if ($verbose) {
+                $output->writeln('Aborting, '.$deployLock.' file present');
+            }
+            return;
+        }
+
+        $doctrine = $this->getContainer()->get('doctrine');
+        $solarium = $this->getContainer()->get('solarium.client');
+        $redis = $this->getContainer()->get('snc_redis.default');
+        $downloadManager = $this->getContainer()->get('packagist.download_manager');
+        $favoriteManager = $this->getContainer()->get('packagist.favorite_manager');
+
+        $lock = new LockHandler('packagist_package_indexer');
+
+        // another dumper is still active
+        if (!$lock->lock()) {
+            if ($verbose) {
+                $output->writeln('Aborting, another indexer is still active');
+            }
+            return;
+        }
+
+        if ($package) {
+            $packages = array(array('id' => $doctrine->getRepository('PackagistWebBundle:Package')->findOneByName($package)->getId()));
+        } elseif ($force || $indexAll) {
+            $packages = $doctrine->getManager()->getConnection()->fetchAll('SELECT id FROM package ORDER BY id ASC');
+            $doctrine->getManager()->getConnection()->executeQuery('UPDATE package SET indexedAt = NULL');
+        } else {
+            $packages = $doctrine->getRepository('PackagistWebBundle:Package')->getStalePackagesForIndexing();
+        }
+
+        $ids = array();
+        foreach ($packages as $row) {
+            $ids[] = $row['id'];
+        }
+
+        // clear index before a full-update
+        if ($force && !$package) {
+            if ($verbose) {
+                $output->writeln('Deleting existing index');
+            }
+
+            $update = $solarium->createUpdate();
+            $update->addDeleteQuery('*:*');
+            $update->addCommit();
+
+            $solarium->update($update);
+        }
+
+        $total = count($ids);
+        $current = 0;
+
+        // update package index
+        while ($ids) {
+            $indexTime = new \DateTime;
+            $idsSlice = array_splice($ids, 0, 50);
+            $packages = $doctrine->getRepository('PackagistWebBundle:Package')->findById($idsSlice);
+            $update = $solarium->createUpdate();
+
+            $indexTimeUpdates = [];
+
+            foreach ($packages as $package) {
+                $current++;
+                if ($verbose) {
+                    $output->writeln('['.sprintf('%'.strlen($total).'d', $current).'/'.$total.'] Indexing '.$package->getName());
+                }
+
+                try {
+                    $document = $update->createDocument();
+                    $tags = $doctrine->getManager()->getConnection()->fetchAll(
+                        'SELECT t.name FROM package p
+                            JOIN package_version pv ON p.id = pv.package_id
+                            JOIN version_tag vt ON vt.version_id = pv.id
+                            JOIN tag t ON t.id = vt.tag_id
+                            WHERE p.id = :id
+                            GROUP BY t.id, t.name',
+                        ['id' => $package->getId()]
+                    );
+                    foreach ($tags as $idx => $tag) {
+                        $tags[$idx] = $tag['name'];
+                    }
+                    $this->updateDocumentFromPackage($document, $package, $tags, $redis, $downloadManager, $favoriteManager);
+                    $update->addDocument($document);
+
+                    $indexTimeUpdates[$indexTime->format('Y-m-d H:i:s')][] = $package->getId();
+                } catch (\Exception $e) {
+                    $output->writeln('<error>Exception: '.$e->getMessage().', skipping package '.$package->getName().'.</error>');
+                }
+
+                $providers = $doctrine->getManager()->getConnection()->fetchAll(
+                    'SELECT lp.packageName
+                        FROM package p
+                        JOIN package_version pv ON p.id = pv.package_id
+                        JOIN link_provide lp ON lp.version_id = pv.id
+                        WHERE p.id = :id
+                        AND pv.development = true
+                        GROUP BY lp.packageName',
+                    ['id' => $package->getId()]
+                );
+                foreach ($providers as $provided) {
+                    $provided = $provided['packageName'];
+                    try {
+                        $document = $update->createDocument();
+                        $document->setField('id', $provided);
+                        $document->setField('name', $provided);
+                        $document->setField('description', '');
+                        $document->setField('type', 'virtual-package');
+                        $document->setField('trendiness', 100);
+                        $document->setField('repository', '');
+                        $document->setField('abandoned', 0);
+                        $document->setField('replacementPackage', '');
+                        $update->addDocument($document);
+                    } catch (\Exception $e) {
+                        $output->writeln('<error>'.get_class($e).': '.$e->getMessage().', skipping package '.$package->getName().':provide:'.$provided.'</error>');
+                    }
+                }
+            }
+
+            try {
+                $update->addCommit();
+                $solarium->update($update);
+            } catch (\Exception $e) {
+                $output->writeln('<error>'.get_class($e).': '.$e->getMessage().', occurred while processing packages: '.implode(',', $idsSlice).'</error>');
+            }
+
+            $doctrine->getManager()->clear();
+            unset($packages);
+
+            if ($verbose) {
+                $output->writeln('Updating package index times');
+            }
+            foreach ($indexTimeUpdates as $dt => $idsToUpdate) {
+                $retries = 5;
+                // retry loop in case of a lock timeout
+                while ($retries--) {
+                    try {
+                        $doctrine->getManager()->getConnection()->executeQuery(
+                            'UPDATE package SET indexedAt=:indexed WHERE id IN (:ids)',
+                            [
+                                'ids' => $idsToUpdate,
+                                'indexed' => $dt,
+                            ],
+                            ['ids' => Connection::PARAM_INT_ARRAY]
+                        );
+                    } catch (\Exception $e) {
+                        if (!$retries) {
+                            throw $e;
+                        }
+                        sleep(2);
+                    }
+                }
+            }
+        }
+
+        $lock->release();
+    }
+
+    private function updateDocumentFromPackage(
+        Solarium_Document_ReadWrite $document,
+        Package $package,
+        array $tags,
+        $redis,
+        DownloadManager $downloadManager,
+        FavoriteManager $favoriteManager
+    ) {
+        $document->setField('id', $package->getId());
+        $document->setField('name', $package->getName());
+        $document->setField('description', preg_replace('{[\x00-\x1f]+}u', '', $package->getDescription()));
+        $document->setField('type', $package->getType());
+        $document->setField('trendiness', $redis->zscore('downloads:trending', $package->getId()));
+        $document->setField('downloads', $downloadManager->getTotalDownloads($package));
+        $document->setField('favers', $favoriteManager->getFaverCount($package));
+        $document->setField('repository', $package->getRepository());
+        $document->setField('language', $package->getLanguage());
+        if ($package->isAbandoned()) {
+            $document->setField('abandoned', 1);
+            $document->setField('replacementPackage', $package->getReplacementPackage() ?: '');
+        } else {
+            $document->setField('abandoned', 0);
+            $document->setField('replacementPackage', '');
+        }
+
+        $tags = array_map(function ($tag) {
+            return mb_strtolower(preg_replace('{[\x00-\x1f]+}u', '', $tag), 'UTF-8');
+        }, $tags);
+        $document->setField('tags', $tags);
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/UpdatePackagesCommand.php b/src/Packagist/WebBundle/Command/UpdatePackagesCommand.php
new file mode 100644
index 0000000..0fb736b
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/UpdatePackagesCommand.php
@@ -0,0 +1,135 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Command;
+
+use Composer\Factory;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Repository\InvalidRepositoryException;
+use Drupal\ParseComposer\Repository as VcsRepository;
+use Packagist\WebBundle\Package\Updater;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class UpdatePackagesCommand extends ContainerAwareCommand
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:update')
+            ->setDefinition(array(
+                new InputOption('force', null, InputOption::VALUE_NONE, 'Force a re-crawl of all packages, or if a package name is given forces an update of all versions'),
+                new InputOption('delete-before', null, InputOption::VALUE_NONE, 'Force deletion of all versions before an update'),
+                new InputOption('notify-failures', null, InputOption::VALUE_NONE, 'Notify failures to maintainers by email'),
+                new InputOption('update-equal-refs', null, InputOption::VALUE_NONE, 'Force update of all versions even when they already exist'),
+                new InputArgument('package', InputArgument::OPTIONAL, 'Package name to update'),
+            ))
+            ->setDescription('Updates packages')
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = FALSE;
+
+        if ($input->hasOption('verbose')) {
+            $verbose = $input->getOption('verbose');
+        }
+        $force = $input->getOption('force');
+        $package = $input->getArgument('package');
+
+        $doctrine = $this->getContainer()->get('doctrine');
+        $router = $this->getContainer()->get('router');
+
+        $flags = 0;
+
+        if ($package) {
+            $packages = array(array('id' => $doctrine->getRepository('PackagistWebBundle:Package')->findOneByName($package)->getId()));
+            $flags = $force ? Updater::UPDATE_EQUAL_REFS : 0;
+        } elseif ($force) {
+            $packages = $doctrine->getManager()->getConnection()->fetchAll('SELECT id FROM package ORDER BY id ASC');
+            $flags = Updater::UPDATE_EQUAL_REFS;
+        } else {
+            $packages = $doctrine->getRepository('PackagistWebBundle:Package')->getStalePackages();
+        }
+
+        $ids = array();
+        foreach ($packages as $package) {
+            $ids[] = $package['id'];
+        }
+
+        if ($input->getOption('delete-before')) {
+            $flags = Updater::DELETE_BEFORE;
+        } elseif ($input->getOption('update-equal-refs')) {
+            $flags = Updater::UPDATE_EQUAL_REFS;
+        }
+
+        $updater = $this->getContainer()->get('packagist.package_updater');
+        $start = new \DateTime();
+
+        if ($verbose && $input->getOption('notify-failures')) {
+            throw new \LogicException('Failures can not be notified in verbose mode since the output is piped to the CLI');
+        }
+
+        $input->setInteractive(false);
+        $config = Factory::createConfig();
+        $io = $verbose ? new ConsoleIO($input, $output, $this->getApplication()->getHelperSet()) : new BufferIO('');
+        $io->loadConfiguration($config);
+        $loader = new ValidatingArrayLoader(new ArrayLoader());
+
+        while ($ids) {
+            $packages = $doctrine->getRepository('PackagistWebBundle:Package')->getPackagesWithVersions(array_splice($ids, 0, 50));
+
+            foreach ($packages as $package) {
+                if ($verbose) {
+                    $output->writeln('Importing '.$package->getRepository());
+                }
+                try {
+                    if (null === $io || $io instanceof BufferIO) {
+                        $io = new BufferIO('');
+                        $io->loadConfiguration($config);
+                    }
+                    $repository = new VcsRepository(array('url' => $package->getRepository()), $io, $config);
+                    $repository->setLoader($loader);
+                    $updater->update($io, $config, $package, $repository, $flags, $start);
+                } catch (InvalidRepositoryException $e) {
+                    $output->writeln('<error>Broken repository in '.$router->generate('view_package', array('name' => $package->getName()), true).': '.$e->getMessage().'</error>');
+                    if ($input->getOption('notify-failures')) {
+                        if (!$this->getContainer()->get('packagist.package_manager')->notifyUpdateFailure($package, $e, $io->getOutput())) {
+                            $output->writeln('<error>Failed to notify maintainers</error>');
+                        }
+                    }
+                } catch (\Exception $e) {
+                    $output->writeln('<error>Error updating '.$router->generate('view_package', array('name' => $package->getName()), true).' ['.get_class($e).']: '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().'</error>');
+                }
+            }
+
+            $doctrine->getManager()->clear();
+            unset($packages);
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/Command/UpsertPackagesCommand.php b/src/Packagist/WebBundle/Command/UpsertPackagesCommand.php
new file mode 100644
index 0000000..23ca4a0
--- /dev/null
+++ b/src/Packagist/WebBundle/Command/UpsertPackagesCommand.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Packagist\WebBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Packagist\WebBundle\Entity\Package;
+use Composer\Repository\VcsRepository;
+use Composer\IO\BufferIO;
+use Composer\IO\ConsoleIO;
+
+class UpsertPackagesCommand extends ContainerAwareCommand
+{
+    protected function configure()
+    {
+        $this
+            ->setName('packagist:upsert')
+            ->setDefinition(array(
+                new InputOption(
+                    'force',
+                    null,
+                    InputOption::VALUE_NONE,
+                    'Overwrite existing packages'
+                ),
+                new InputOption(
+                    'vendor',
+                    null,
+                    InputOption::VALUE_OPTIONAL,
+                    'default vendor name'
+                ),
+                new InputOption(
+                    'repo-pattern',
+                    null,
+                    InputOption::VALUE_OPTIONAL,
+                    'pattern for repo url',
+                    'https://github.com/%s'
+                ),
+                new InputArgument(
+                    'packages',
+                    InputArgument::REQUIRED|InputArgument::IS_ARRAY,
+                    'list of packages to add'
+                )
+            ))->setDescription('Imports packages from packages.json');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $verbose = $input->getOption('verbose');
+        $packageArgs = $input->getArgument('packages');
+        $packages = [];
+        foreach ($packageArgs as $key => $packageArg) {
+            if (file_exists($packageArg)) {
+                $packageArg = preg_split(
+                    '/\s+/',
+                    file_get_contents($packageArg)
+                );
+            }
+            else {
+                $packageArg = [$packageArg];
+            }
+            $packages = array_merge($packages, $packageArg);
+        }
+        $io = $verbose
+            ? new ConsoleIO(
+                $input,
+                $output,
+                $this->getApplication()->getHelperSet()
+            )
+            : new BufferIO('');
+        foreach ($packages as $name) {
+            $fullName = $name;
+            if ($vendor = $input->getOption('vendor')) {
+                $fullName = "$vendor/$name";
+            }
+            $io->write('upserting '.$fullName);
+            try {
+                $this->getContainer()->get('packagist.package_upserter')
+                    ->execute(
+                        sprintf(
+                            $input->getOption('repo-pattern'),
+                            $fullName,
+                            $name,
+                            $vendor
+                        ),
+                        $fullName,
+                        $io
+                    );
+            }
+            catch (\Exception $e) {
+                if ($io instanceof BufferIO) {
+                    echo $io->getOutput();
+                }
+                throw $e;
+            }
+            if ($io instanceof BufferIO) {
+                echo $io->getOutput();
+            }
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/AboutController.php b/src/Packagist/WebBundle/Controller/AboutController.php
new file mode 100644
index 0000000..787a612
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/AboutController.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class AboutController extends Controller
+{
+    /**
+     * @Template()
+     * @Route("/about", name="about")
+     */
+    public function aboutAction()
+    {
+        return array();
+    }
+
+    /**
+     * @Route("/about-composer")
+     */
+    public function aboutComposerFallbackAction()
+    {
+        return new RedirectResponse('https://getcomposer.org/', 301);
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/ApiController.php b/src/Packagist/WebBundle/Controller/ApiController.php
new file mode 100644
index 0000000..05e16e9
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/ApiController.php
@@ -0,0 +1,340 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Composer\Console\HtmlOutputFormatter;
+use Composer\Factory;
+use Composer\IO\BufferIO;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Repository\InvalidRepositoryException;
+use Composer\Repository\VcsRepository;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\User;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ApiController extends Controller
+{
+    /**
+     * @Route("/packages.json", name="packages", defaults={"_format" = "json"})
+     * @Method({"GET"})
+     */
+    public function packagesAction()
+    {
+        // fallback if any of the dumped files exist
+        $rootJson = $this->container->getParameter('kernel.root_dir').'/../web/packages_root.json';
+        if (file_exists($rootJson)) {
+            return new Response(file_get_contents($rootJson));
+        }
+        $rootJson = $this->container->getParameter('kernel.root_dir').'/../web/packages.json';
+        if (file_exists($rootJson)) {
+            return new Response(file_get_contents($rootJson));
+        }
+
+        $this->get('logger')->alert('packages.json is missing and the fallback controller is being hit, you need to use app/console packagist:dump');
+
+        return new Response('Horrible misconfiguration or the dumper script messed up, you need to use app/console packagist:dump', 404);
+    }
+
+    /**
+     * @Route("/api/create-package", name="generic_create", defaults={"_format" = "json"})
+     * @Method({"POST"})
+     */
+    public function createPackageAction(Request $request)
+    {
+        $payload = json_decode($request->getContent(), true);
+        if (!$payload) {
+            return new JsonResponse(array('status' => 'error', 'message' => 'Missing payload parameter'), 406);
+        }
+        $url = $payload['repository']['url'];
+        $package = new Package;
+        $package->setEntityRepository($this->getDoctrine()->getRepository('PackagistWebBundle:Package'));
+        $package->setRouter($this->get('router'));
+        $user = $this->findUser($request);
+        $package->addMaintainer($user);
+        $package->setRepository($url);
+        $errors = $this->get('validator')->validate($package);
+        if (count($errors) > 0) {
+            $errorArray = array();
+            foreach ($errors as $error) {
+                $errorArray[$error->getPropertyPath()] =  $error->getMessage();
+            }
+            return new JsonResponse(array('status' => 'error', 'message' => $errorArray), 406);
+        }
+        try {
+            $em = $this->getDoctrine()->getManager();
+            $em->persist($package);
+            $em->flush();
+        } catch (\Exception $e) {
+            $this->get('logger')->critical($e->getMessage(), array('exception', $e));
+            return new JsonResponse(array('status' => 'error', 'message' => 'Error saving package'), 500);
+        }
+
+        return new JsonResponse(array('status' => 'success'), 202);
+    }
+
+    /**
+     * @Route("/api/update-package", name="generic_postreceive", defaults={"_format" = "json"})
+     * @Route("/api/github", name="github_postreceive", defaults={"_format" = "json"})
+     * @Route("/api/bitbucket", name="bitbucket_postreceive", defaults={"_format" = "json"})
+     * @Method({"POST"})
+     */
+    public function updatePackageAction(Request $request)
+    {
+        // parse the payload
+        $payload = json_decode($request->request->get('payload'), true);
+        if (!$payload && $request->headers->get('Content-Type') === 'application/json') {
+            $payload = json_decode($request->getContent(), true);
+        }
+
+        if (!$payload) {
+            return new JsonResponse(array('status' => 'error', 'message' => 'Missing payload parameter'), 406);
+        }
+
+        if (isset($payload['repository']['url'])) { // github/gitlab/anything hook
+            $urlRegex = '{^(?:ssh://git@|https?://|git://|git@)?(?P<host>[a-z0-9.-]+)(?::[0-9]+/|[:/])(?P<path>[\w.-]+/[\w.-]+?)(?:\.git|/)?$}i';
+            $url = $payload['repository']['url'];
+        } elseif (isset($payload['repository']['links']['html']['href'])) { // bitbucket push event payload
+            $urlRegex = '{^(?:https?://|git://|git@)?(?:api\.)?(?P<host>bitbucket\.org)[/:](?P<path>[\w.-]+/[\w.-]+?)(\.git)?/?$}i';
+            $url = $payload['repository']['links']['html']['href'];
+        } elseif (isset($payload['canon_url']) && isset($payload['repository']['absolute_url'])) { // bitbucket post hook (deprecated)
+            $urlRegex = '{^(?:https?://|git://|git@)?(?P<host>bitbucket\.org)[/:](?P<path>[\w.-]+/[\w.-]+?)(\.git)?/?$}i';
+            $url = $payload['canon_url'].$payload['repository']['absolute_url'];
+        } else {
+            return new JsonResponse(array('status' => 'error', 'message' => 'Missing or invalid payload'), 406);
+        }
+
+        return $this->receivePost($request, $url, $urlRegex);
+    }
+
+    /**
+     * @Route("/downloads/{name}", name="track_download", requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"}, defaults={"_format" = "json"})
+     * @Method({"POST"})
+     */
+    public function trackDownloadAction(Request $request, $name)
+    {
+        $result = $this->getPackageAndVersionId($name, $request->request->get('version_normalized'));
+
+        if (!$result) {
+            return new JsonResponse(array('status' => 'error', 'message' => 'Package not found'), 200);
+        }
+
+        $this->trackDownload($result['id'], $result['vid'], $request->getClientIp());
+
+        return new JsonResponse(array('status' => 'success'), 201);
+    }
+
+    /**
+     * Expects a json like:
+     *
+     * {
+     *     "downloads": [
+     *         {"name": "foo/bar", "version": "1.0.0.0"},
+     *         // ...
+     *     ]
+     * }
+     *
+     * The version must be the normalized one
+     *
+     * @Route("/downloads/", name="track_download_batch", defaults={"_format" = "json"})
+     * @Method({"POST"})
+     */
+    public function trackDownloadsAction(Request $request)
+    {
+        $contents = json_decode($request->getContent(), true);
+        if (empty($contents['downloads']) || !is_array($contents['downloads'])) {
+            return new JsonResponse(array('status' => 'error', 'message' => 'Invalid request format, must be a json object containing a downloads key filled with an array of name/version objects'), 200);
+        }
+
+        $failed = array();
+        foreach ($contents['downloads'] as $package) {
+            $result = $this->getPackageAndVersionId($package['name'], $package['version']);
+
+            if (!$result) {
+                $failed[] = $package;
+                continue;
+            }
+
+            $this->trackDownload($result['id'], $result['vid'], $request->getClientIp());
+        }
+
+        if ($failed) {
+            return new JsonResponse(array('status' => 'partial', 'message' => 'Packages '.json_encode($failed).' not found'), 200);
+        }
+
+        return new JsonResponse(array('status' => 'success'), 201);
+    }
+
+    /**
+     * @param string $name
+     * @param string $version
+     * @return array
+     */
+    protected function getPackageAndVersionId($name, $version)
+    {
+        return $this->get('doctrine.dbal.default_connection')->fetchAssoc(
+            'SELECT p.id, v.id vid
+            FROM package p
+            LEFT JOIN package_version v ON p.id = v.package_id
+            WHERE p.name = ?
+            AND v.normalizedVersion = ?
+            LIMIT 1',
+            array($name, $version)
+        );
+    }
+
+    protected function trackDownload($id, $vid, $ip)
+    {
+        $redis = $this->get('snc_redis.default');
+        $manager = $this->get('packagist.download_manager');
+
+        $throttleKey = 'dl:'.$id.':'.$ip.':'.date('Ymd');
+        $requests = $redis->incr($throttleKey);
+        if (1 === $requests) {
+            $redis->expire($throttleKey, 86400);
+        }
+        if ($requests <= 10) {
+            $manager->addDownload($id, $vid);
+        }
+    }
+
+    /**
+     * Perform the package update
+     *
+     * @param Request $request the current request
+     * @param string $url the repository's URL (deducted from the request)
+     * @param string $urlRegex the regex used to split the user packages into domain and path
+     * @return Response
+     */
+    protected function receivePost(Request $request, $url, $urlRegex)
+    {
+        // try to parse the URL first to avoid the DB lookup on malformed requests
+        if (!preg_match($urlRegex, $url)) {
+            return new Response(json_encode(array('status' => 'error', 'message' => 'Could not parse payload repository URL')), 406);
+        }
+
+        // find the user
+        $user = $this->findUser($request);
+
+        if (!$user) {
+            return new Response(json_encode(array('status' => 'error', 'message' => 'Invalid credentials')), 403);
+        }
+
+        // try to find the user package
+        $packages = $this->findPackagesByUrl($user, $url, $urlRegex);
+
+        if (!$packages) {
+            return new Response(json_encode(array('status' => 'error', 'message' => 'Could not find a package that matches this request (does user maintain the package?)')), 404);
+        }
+
+        // don't die if this takes a while
+        set_time_limit(3600);
+
+        // put both updating the database and scanning the repository in a transaction
+        $em = $this->get('doctrine.orm.entity_manager');
+        $updater = $this->get('packagist.package_updater');
+        $config = Factory::createConfig();
+        $io = new BufferIO('', OutputInterface::VERBOSITY_VERY_VERBOSE, new HtmlOutputFormatter(Factory::createAdditionalStyles()));
+        $io->loadConfiguration($config);
+
+        try {
+            /** @var Package $package */
+            foreach ($packages as $package) {
+                $em->transactional(function($em) use ($package, $updater, $io, $config) {
+                    // prepare dependencies
+                    $loader = new ValidatingArrayLoader(new ArrayLoader());
+
+                    // prepare repository
+                    $repository = new VcsRepository(array('url' => $package->getRepository()), $io, $config);
+                    $repository->setLoader($loader);
+
+                    // perform the actual update (fetch and re-scan the repository's source)
+                    $updater->update($io, $config, $package, $repository);
+
+                    // update the package entity
+                    $package->setAutoUpdated(true);
+                    $em->flush();
+                });
+            }
+        } catch (\Exception $e) {
+            if ($e instanceof InvalidRepositoryException) {
+                $this->get('packagist.package_manager')->notifyUpdateFailure($package, $e, $io->getOutput());
+            }
+
+            return new Response(json_encode(array(
+                'status' => 'error',
+                'message' => '['.get_class($e).'] '.$e->getMessage(),
+                'details' => '<pre>'.$io->getOutput().'</pre>'
+            )), 400);
+        }
+
+        return new JsonResponse(array('status' => 'success'), 202);
+    }
+
+    /**
+     * Find a user by his username and API token
+     *
+     * @param Request $request
+     * @return User|null the found user or null otherwise
+     */
+    protected function findUser(Request $request)
+    {
+        $username = $request->request->has('username') ?
+            $request->request->get('username') :
+            $request->query->get('username');
+
+        $apiToken = $request->request->has('apiToken') ?
+            $request->request->get('apiToken') :
+            $request->query->get('apiToken');
+
+        $user = $this->get('packagist.user_repository')
+            ->findOneBy(array('username' => $username, 'apiToken' => $apiToken));
+
+        return $user;
+    }
+
+    /**
+     * Find a user package given by its full URL
+     *
+     * @param User $user
+     * @param string $url
+     * @param string $urlRegex
+     * @return array the packages found
+     */
+    protected function findPackagesByUrl(User $user, $url, $urlRegex)
+    {
+        if (!preg_match($urlRegex, $url, $matched)) {
+            return array();
+        }
+
+        $packages = array();
+        foreach ($user->getPackages() as $package) {
+            if (preg_match($urlRegex, $package->getRepository(), $candidate)
+                && strtolower($candidate['host']) === strtolower($matched['host'])
+                && strtolower($candidate['path']) === strtolower($matched['path'])
+            ) {
+                $packages[] = $package;
+            }
+        }
+
+        return $packages;
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/ApiDocController.php b/src/Packagist/WebBundle/Controller/ApiDocController.php
new file mode 100644
index 0000000..9c39090
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/ApiDocController.php
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be> 
+ */
+class ApiDocController extends Controller
+{
+    /**
+     * @Template()
+     * @Route("/apidoc", name="api_doc")
+     */
+    public function indexAction()
+    {
+        return array();
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/Controller.php b/src/Packagist/WebBundle/Controller/Controller.php
new file mode 100644
index 0000000..ec4d26d
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/Controller.php
@@ -0,0 +1,83 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller as BaseController;
+use Pagerfanta\Adapter\DoctrineORMAdapter;
+use Pagerfanta\Pagerfanta;
+use Packagist\WebBundle\Entity\Package;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Controller extends BaseController
+{
+    protected function getPackagesMetadata($packages)
+    {
+        $favMgr = $this->get('packagist.favorite_manager');
+        $dlMgr = $this->get('packagist.download_manager');
+
+        try {
+            $ids = array();
+
+            if (!count($packages)) {
+                return;
+            }
+
+            $favs = array();
+            $solarium = false;
+            foreach ($packages as $package) {
+                if ($package instanceof \Solarium_Document_ReadOnly) {
+                    $solarium = true;
+                    $ids[] = $package->id;
+                } elseif ($package instanceof Package) {
+                    $ids[] = $package->getId();
+                    $favs[$package->getId()] = $favMgr->getFaverCount($package);
+                } elseif (is_array($package)) {
+                    $solarium = true;
+                    $ids[] = $package['id'];
+                } else {
+                    throw new \LogicException('Got invalid package entity');
+                }
+            }
+
+            if ($solarium) {
+                return array(
+                    'downloads' => $dlMgr->getPackagesDownloads($ids),
+                    'favers' => $favMgr->getFaverCounts($ids),
+                );
+            }
+
+            return array(
+                'downloads' => $dlMgr->getPackagesDownloads($ids),
+                'favers' => $favs,
+            );
+        } catch (\Predis\Connection\ConnectionException $e) {}
+    }
+
+    /**
+     * Initializes the pager for a query.
+     *
+     * @param \Doctrine\ORM\QueryBuilder $query Query for packages
+     * @param int                        $page  Pagenumber to retrieve.
+     * @return \Pagerfanta\Pagerfanta
+     */
+    protected function setupPager($query, $page)
+    {
+        $paginator = new Pagerfanta(new DoctrineORMAdapter($query, true));
+        $paginator->setMaxPerPage(15);
+        $paginator->setCurrentPage($page, false, true);
+
+        return $paginator;
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/ExploreController.php b/src/Packagist/WebBundle/Controller/ExploreController.php
new file mode 100644
index 0000000..7f2b82d
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/ExploreController.php
@@ -0,0 +1,152 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Doctrine\DBAL\ConnectionException;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\PackageRepository;
+use Packagist\WebBundle\Entity\VersionRepository;
+use Pagerfanta\Adapter\FixedAdapter;
+use Pagerfanta\Pagerfanta;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+
+/**
+ * @Route("/explore")
+ */
+class ExploreController extends Controller
+{
+    /**
+     * @Template()
+     * @Route("/", name="browse")
+     */
+    public function exploreAction()
+    {
+        /** @var PackageRepository $pkgRepo */
+        $pkgRepo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
+        /** @var VersionRepository $verRepo */
+        $verRepo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
+        $newSubmitted = $pkgRepo->getQueryBuilderForNewestPackages()->setMaxResults(10)
+            ->getQuery()->useResultCache(true, 900, 'new_submitted_packages')->getResult();
+        $newReleases = $verRepo->getLatestReleases(10);
+        $randomIds = $this->getDoctrine()->getConnection()->fetchAll('SELECT id FROM package ORDER BY RAND() LIMIT 10');
+        $random = $pkgRepo->createQueryBuilder('p')->where('p.id IN (:ids)')->setParameter('ids', $randomIds)->getQuery()->getResult();
+        try {
+            $popular = array();
+            $popularIds = $this->get('snc_redis.default')->zrevrange('downloads:trending', 0, 9);
+            if ($popularIds) {
+                $popular = $pkgRepo->createQueryBuilder('p')->where('p.id IN (:ids)')->setParameter('ids', $popularIds)
+                    ->getQuery()->useResultCache(true, 900, 'popular_packages')->getResult();
+                usort($popular, function ($a, $b) use ($popularIds) {
+                    return array_search($a->getId(), $popularIds) > array_search($b->getId(), $popularIds) ? 1 : -1;
+                });
+            }
+        } catch (ConnectionException $e) {
+            $popular = array();
+        }
+
+        $data = array(
+            'newlySubmitted' => $newSubmitted,
+            'newlyReleased' => $newReleases,
+            'random' => $random,
+            'popular' => $popular,
+        );
+
+        return $data;
+    }
+
+    /**
+     * @Template()
+     * @Route("/popular.{_format}", name="browse_popular", defaults={"_format"="html"})
+     * @Cache(smaxage=900)
+     */
+    public function popularAction(Request $req)
+    {
+        try {
+            $redis = $this->get('snc_redis.default');
+            $perPage = $req->query->getInt('per_page', 15);
+            if ($perPage <= 0 || $perPage > 100) {
+                if ($req->getRequestFormat() === 'json') {
+                    return new JsonResponse(array(
+                        'status' => 'error',
+                        'message' => 'The optional packages per_page parameter must be an integer between 1 and 100 (default: 15)',
+                    ), 400);
+                }
+
+                $perPage = max(0, min(100, $perPage));
+            }
+
+            $popularIds = $redis->zrevrange(
+                'downloads:trending',
+                ($req->get('page', 1) - 1) * $perPage,
+                $req->get('page', 1) * $perPage - 1
+            );
+            $popular = $this->getDoctrine()->getRepository('PackagistWebBundle:Package')
+                ->createQueryBuilder('p')->where('p.id IN (:ids)')->setParameter('ids', $popularIds)
+                ->getQuery()->useResultCache(true, 900, 'popular_packages')->getResult();
+            usort($popular, function ($a, $b) use ($popularIds) {
+                return array_search($a->getId(), $popularIds) > array_search($b->getId(), $popularIds) ? 1 : -1;
+            });
+
+            $packages = new Pagerfanta(new FixedAdapter($redis->zcard('downloads:trending'), $popular));
+            $packages->setMaxPerPage($perPage);
+            $packages->setCurrentPage($req->get('page', 1), false, true);
+        } catch (ConnectionException $e) {
+            $packages = new Pagerfanta(new FixedAdapter(0, array()));
+        }
+
+        $data = array(
+            'packages' => $packages,
+        );
+        $data['meta'] = $this->getPackagesMetadata($data['packages']);
+
+        if ($req->getRequestFormat() === 'json') {
+            $result = array(
+                'packages' => array(),
+                'total' => $packages->getNbResults(),
+            );
+
+            /** @var Package $package */
+            foreach ($packages as $package) {
+                $url = $this->generateUrl('view_package', array('name' => $package->getName()), UrlGeneratorInterface::ABSOLUTE_URL);
+
+                $result['packages'][] = array(
+                    'name' => $package->getName(),
+                    'description' => $package->getDescription() ?: '',
+                    'url' => $url,
+                    'downloads' => $data['meta']['downloads'][$package->getId()],
+                    'favers' => $data['meta']['favers'][$package->getId()],
+                );
+            }
+
+            if ($packages->hasNextPage()) {
+                $params = array(
+                    '_format' => 'json',
+                    'page' => $packages->getNextPage(),
+                );
+                if ($perPage !== 15) {
+                    $params['per_page'] = $perPage;
+                }
+                $result['next'] = $this->generateUrl('browse_popular', $params, UrlGeneratorInterface::ABSOLUTE_URL);
+            }
+
+            return new JsonResponse($result);
+        }
+
+        return $data;
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/FeedController.php b/src/Packagist/WebBundle/Controller/FeedController.php
new file mode 100644
index 0000000..454fc55
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/FeedController.php
@@ -0,0 +1,289 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Doctrine\ORM\QueryBuilder;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\Version;
+use Pagerfanta\Adapter\DoctrineORMAdapter;
+use Pagerfanta\Pagerfanta;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Zend\Feed\Writer\Entry;
+use Zend\Feed\Writer\Feed;
+
+/**
+ * @author Rafael Dohms <rafael@doh.ms>
+ *
+ * @Route("/feeds")
+ */
+class FeedController extends Controller
+{
+    /**
+     * @Route("/", name="feeds")
+     * @Template
+     */
+    public function feedsAction()
+    {
+        return array();
+    }
+
+    /**
+     * @Route(
+     *     "/packages.{_format}",
+     *     name="feed_packages",
+     *     requirements={"_format"="(rss|atom)"}
+     * )
+     * @Method({"GET"})
+     */
+    public function packagesAction(Request $req)
+    {
+        /** @var $repo \Packagist\WebBundle\Entity\PackageRepository */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
+        $packages = $this->getLimitedResults(
+            $repo->getQueryBuilderForNewestPackages()
+        );
+
+        $feed = $this->buildFeed(
+            $req,
+            'Newly Submitted Packages',
+            'Latest packages submitted to Packagist.',
+            $this->generateUrl('browse', array(), UrlGeneratorInterface::ABSOLUTE_URL),
+            $packages
+        );
+
+        return $this->buildResponse($req, $feed);
+    }
+
+    /**
+     * @Route(
+     *     "/releases.{_format}",
+     *     name="feed_releases",
+     *     requirements={"_format"="(rss|atom)"}
+     * )
+     * @Method({"GET"})
+     */
+    public function releasesAction(Request $req)
+    {
+        /** @var $repo \Packagist\WebBundle\Entity\VersionRepository */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
+        $packages = $this->getLimitedResults(
+            $repo->getQueryBuilderForLatestVersionWithPackage()
+        );
+
+        $feed = $this->buildFeed(
+            $req,
+            'New Releases',
+            'Latest releases of all packages.',
+            $this->generateUrl('browse', array(), UrlGeneratorInterface::ABSOLUTE_URL),
+            $packages
+        );
+
+        return $this->buildResponse($req, $feed);
+    }
+
+    /**
+     * @Route(
+     *     "/vendor.{vendor}.{_format}",
+     *     name="feed_vendor",
+     *     requirements={"_format"="(rss|atom)", "vendor"="[A-Za-z0-9_.-]+"}
+     * )
+     * @Method({"GET"})
+     */
+    public function vendorAction(Request $req, $vendor)
+    {
+        /** @var $repo \Packagist\WebBundle\Entity\VersionRepository */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
+        $packages = $this->getLimitedResults(
+            $repo->getQueryBuilderForLatestVersionWithPackage($vendor)
+        );
+
+        $feed = $this->buildFeed(
+            $req,
+            "$vendor packages",
+            "Latest packages updated on Packagist of $vendor.",
+            $this->generateUrl('view_vendor', array('vendor' => $vendor), UrlGeneratorInterface::ABSOLUTE_URL),
+            $packages
+        );
+
+        return $this->buildResponse($req, $feed);
+    }
+
+    /**
+     * @Route(
+     *     "/package.{package}.{_format}",
+     *     name="feed_package",
+     *     requirements={"_format"="(rss|atom)", "package"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"}
+     * )
+     * @Method({"GET"})
+     */
+    public function packageAction(Request $req, $package)
+    {
+        /** @var $repo \Packagist\WebBundle\Entity\VersionRepository */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
+        $packages = $this->getLimitedResults(
+            $repo->getQueryBuilderForLatestVersionWithPackage(null, $package)
+        );
+
+        $feed = $this->buildFeed(
+            $req,
+            "$package releases",
+            "Latest releases on Packagist of $package.",
+            $this->generateUrl('view_package', array('name' => $package), UrlGeneratorInterface::ABSOLUTE_URL),
+            $packages
+        );
+
+        return $this->buildResponse($req, $feed);
+    }
+
+    /**
+     * Limits a query to the desired number of results
+     *
+     * @param \Doctrine\ORM\QueryBuilder $queryBuilder
+     *
+     * @return array|\Traversable
+     */
+    protected function getLimitedResults(QueryBuilder $queryBuilder)
+    {
+        $paginator = new Pagerfanta(new DoctrineORMAdapter($queryBuilder));
+        $paginator->setMaxPerPage(
+            $this->container->getParameter('packagist_web.rss_max_items')
+        );
+        $paginator->setCurrentPage(1);
+
+        return $paginator->getCurrentPageResults();
+    }
+
+    /**
+     * Builds the desired feed
+     *
+     * @param string $title
+     * @param string $description
+     * @param array  $items
+     *
+     * @return \Zend\Feed\Writer\Feed
+     */
+    protected function buildFeed(Request $req, $title, $description, $url, $items)
+    {
+        $feed = new Feed();
+        $feed->setTitle($title);
+        $feed->setDescription($description);
+        $feed->setLink($url);
+        $feed->setGenerator('Packagist');
+
+        foreach ($items as $item) {
+            $entry = $feed->createEntry();
+            $this->populateEntry($entry, $item);
+            $feed->addEntry($entry);
+        }
+
+        if ($req->getRequestFormat() == 'atom') {
+            $feed->setFeedLink(
+                $req->getUri(),
+                $req->getRequestFormat()
+            );
+        }
+
+        if ($feed->count()) {
+            $feed->setDateModified($feed->getEntry(0)->getDateModified());
+        } else {
+            $feed->setDateModified(new \DateTime());
+        }
+
+        return $feed;
+    }
+
+    /**
+     * Receives either a Package or a Version and populates a feed entry.
+     *
+     * @param \Zend\Feed\Writer\Entry $entry
+     * @param Package|Version         $item
+     */
+    protected function populateEntry(Entry $entry, $item)
+    {
+        if ($item instanceof Package) {
+            $this->populatePackageData($entry, $item);
+        } elseif ($item instanceof Version) {
+            $this->populatePackageData($entry, $item->getPackage());
+            $this->populateVersionData($entry, $item);
+        }
+    }
+
+    /**
+     * Populates a feed entry with data coming from Package objects.
+     *
+     * @param \Zend\Feed\Writer\Entry $entry
+     * @param Package                 $package
+     */
+    protected function populatePackageData(Entry $entry, Package $package)
+    {
+        $entry->setTitle($package->getName());
+        $entry->setLink(
+            $this->generateUrl(
+                'view_package',
+                array('name' => $package->getName()),
+                UrlGeneratorInterface::ABSOLUTE_URL
+            )
+        );
+        $entry->setId($package->getName());
+
+        $entry->setDateModified($package->getCreatedAt());
+        $entry->setDateCreated($package->getCreatedAt());
+        $entry->setDescription($package->getDescription() ?: ' ');
+    }
+
+    /**
+     * Populates a feed entry with data coming from Version objects.
+     *
+     * @param \Zend\Feed\Writer\Entry $entry
+     * @param Version                 $version
+     */
+    protected function populateVersionData(Entry $entry, Version $version)
+    {
+        $entry->setTitle($entry->getTitle()." ({$version->getVersion()})");
+        $entry->setId($entry->getId().' '.$version->getVersion());
+
+        $entry->setDateModified($version->getReleasedAt());
+        $entry->setDateCreated($version->getReleasedAt());
+
+        foreach ($version->getAuthors() as $author) {
+            /** @var $author \Packagist\WebBundle\Entity\Author */
+            if ($author->getName()) {
+                $entry->addAuthor(array(
+                    'name' => $author->getName()
+                ));
+            }
+        }
+    }
+
+    /**
+     * Creates a HTTP Response and exports feed
+     *
+     * @param \Zend\Feed\Writer\Feed $feed
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    protected function buildResponse(Request $req, Feed $feed)
+    {
+        $content = $feed->export($req->getRequestFormat());
+
+        $response = new Response($content, 200);
+        $response->setSharedMaxAge(3600);
+
+        return $response;
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/PackageController.php b/src/Packagist/WebBundle/Controller/PackageController.php
new file mode 100644
index 0000000..2eaa635
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/PackageController.php
@@ -0,0 +1,1147 @@
+<?php
+
+namespace Packagist\WebBundle\Controller;
+
+use Composer\Factory;
+use Composer\IO\BufferIO;
+use Symfony\Component\Console\Output\OutputInterface;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Console\HtmlOutputFormatter;
+use Composer\Repository\VcsRepository;
+use Doctrine\ORM\NoResultException;
+use Packagist\WebBundle\Entity\PackageRepository;
+use Packagist\WebBundle\Entity\VersionRepository;
+use Packagist\WebBundle\Form\Model\MaintainerRequest;
+use Packagist\WebBundle\Form\Type\AbandonedType;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\Version;
+use Packagist\WebBundle\Form\Type\AddMaintainerRequestType;
+use Packagist\WebBundle\Form\Type\PackageType;
+use Packagist\WebBundle\Form\Type\RemoveMaintainerRequestType;
+use Predis\Connection\ConnectionException;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+use Composer\Package\Version\VersionParser;
+use DateTimeImmutable;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Pagerfanta\Adapter\FixedAdapter;
+use Pagerfanta\Pagerfanta;
+use Packagist\WebBundle\Package\Updater;
+
+class PackageController extends Controller
+{
+    /**
+     * @Template("PackagistWebBundle:Package:browse.html.twig")
+     * @Route("/packages/", name="allPackages")
+     * @Cache(smaxage=900)
+     */
+    public function allAction(Request $req)
+    {
+        $filters = array(
+            'type' => $req->query->get('type'),
+            'tag' => $req->query->get('tag'),
+        );
+
+        $data = $filters;
+        $page = $req->query->get('page', 1);
+
+        $packages = $this->getDoctrine()
+            ->getRepository('PackagistWebBundle:Package')
+            ->getFilteredQueryBuilder($filters);
+
+        $data['packages'] = $this->setupPager($packages, $page);
+        $data['meta'] = $this->getPackagesMetadata($data['packages']);
+
+        return $data;
+    }
+
+    /**
+     * @Route("/packages/list.json", name="list", defaults={"_format"="json"})
+     * @Method({"GET"})
+     * @Cache(smaxage=300)
+     */
+    public function listAction(Request $req)
+    {
+        /** @var PackageRepository $repo */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
+        $fields = (array) $req->query->get('fields', array());
+        $fields = array_intersect($fields, array('repository', 'type'));
+
+        if ($fields) {
+            $filters = array_filter(array(
+                'type' => $req->query->get('type'),
+                'vendor' => $req->query->get('vendor'),
+            ));
+
+            return new JsonResponse(array('packages' => $repo->getPackagesWithFields($filters, $fields)));
+        }
+
+        if ($req->query->get('type')) {
+            $names = $repo->getPackageNamesByType($req->query->get('type'));
+        } elseif ($req->query->get('vendor')) {
+            $names = $repo->getPackageNamesByVendor($req->query->get('vendor'));
+        } else {
+            $names = $repo->getPackageNames();
+        }
+
+        return new JsonResponse(array('packageNames' => $names));
+    }
+
+    /**
+     * @Template()
+     * @Route("/packages/submit", name="submit")
+     */
+    public function submitPackageAction(Request $req)
+    {
+        $package = new Package;
+        $package->setEntityRepository($this->getDoctrine()->getRepository('PackagistWebBundle:Package'));
+        $package->setRouter($this->get('router'));
+        $form = $this->createForm(PackageType::class, $package, [
+            'action' => $this->generateUrl('submit'),
+        ]);
+        $user = $this->getUser();
+        $package->addMaintainer($user);
+
+        $form->handleRequest($req);
+        if ($form->isValid()) {
+            try {
+                $em = $this->getDoctrine()->getManager();
+                $em->persist($package);
+                $em->flush();
+
+                $this->get('session')->getFlashBag()->set('success', $package->getName().' has been added to the package list, the repository will now be crawled.');
+
+                return new RedirectResponse($this->generateUrl('view_package', array('name' => $package->getName())));
+            } catch (\Exception $e) {
+                $this->get('logger')->critical($e->getMessage(), array('exception', $e));
+                $this->get('session')->getFlashBag()->set('error', $package->getName().' could not be saved.');
+            }
+        }
+
+        return array('form' => $form->createView(), 'page' => 'submit');
+    }
+
+    /**
+     * @Route("/packages/fetch-info", name="submit.fetch_info", defaults={"_format"="json"})
+     */
+    public function fetchInfoAction(Request $req)
+    {
+        $package = new Package;
+        $package->setEntityRepository($this->getDoctrine()->getRepository('PackagistWebBundle:Package'));
+        $package->setRouter($this->get('router'));
+        $form = $this->createForm(new PackageType, $package);
+        $user = $this->getUser();
+        $package->addMaintainer($user);
+
+        $form->handleRequest($req);
+        if ($form->isValid()) {
+            list(, $name) = explode('/', $package->getName(), 2);
+
+            $existingPackages = $this->getDoctrine()
+                ->getRepository('PackagistWebBundle:Package')
+                ->createQueryBuilder('p')
+                ->where('p.name LIKE ?0')
+                ->setParameters(array('%/'.$name))
+                ->getQuery()
+                ->getResult();
+
+            $similar = array();
+
+            /** @var Package $existingPackage */
+            foreach ($existingPackages as $existingPackage) {
+                $similar[] = array(
+                    'name' => $existingPackage->getName(),
+                    'url' => $this->generateUrl('view_package', array('name' => $existingPackage->getName()), true),
+                );
+            }
+
+            return new JsonResponse(array('status' => 'success', 'name' => $package->getName(), 'similar' => $similar));
+        }
+
+        if ($form->isSubmitted()) {
+            $errors = array();
+            if (count($form->getErrors())) {
+                foreach ($form->getErrors() as $error) {
+                    $errors[] = $error->getMessageTemplate();
+                }
+            }
+            foreach ($form->all() as $child) {
+                if (count($child->getErrors())) {
+                    foreach ($child->getErrors() as $error) {
+                        $errors[] = $error->getMessageTemplate();
+                    }
+                }
+            }
+
+            return new JsonResponse(array('status' => 'error', 'reason' => $errors));
+        }
+
+        return new JsonResponse(array('status' => 'error', 'reason' => 'No data posted.'));
+    }
+
+    /**
+     * @Template()
+     * @Route("/packages/{vendor}/", name="view_vendor", requirements={"vendor"="[A-Za-z0-9_.-]+"})
+     */
+    public function viewVendorAction($vendor)
+    {
+        $packages = $this->getDoctrine()
+            ->getRepository('PackagistWebBundle:Package')
+            ->createQueryBuilder('p')
+            ->where('p.name LIKE ?0')
+            ->setParameters(array($vendor.'/%'))
+            ->getQuery()
+            ->getResult();
+
+        if (!$packages) {
+            return $this->redirect($this->generateUrl('search', array('q' => $vendor, 'reason' => 'vendor_not_found')));
+        }
+
+        return array(
+            'packages' => $packages,
+            'meta' => $this->getPackagesMetadata($packages),
+            'vendor' => $vendor,
+            'paginate' => false,
+        );
+    }
+
+    /**
+     * @Route(
+     *     "/p/{name}.{_format}",
+     *     name="view_package_alias",
+     *     requirements={"name"="[A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+?)?", "_format"="(json)"},
+     *     defaults={"_format"="html"}
+     * )
+     * @Route(
+     *     "/packages/{name}",
+     *     name="view_package_alias2",
+     *     requirements={"name"="[A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+?)?/"},
+     *     defaults={"_format"="html"}
+     * )
+     * @Method({"GET"})
+     */
+    public function viewPackageAliasAction(Request $req, $name)
+    {
+        $format = $req->getRequestFormat();
+        if ($format === 'html') {
+            $format = null;
+        }
+        if ($format === 'json' || (!$format && substr($name, -5) === '.json')) {
+            throw new NotFoundHttpException('Package not found');
+        }
+        if (false === strpos(trim($name, '/'), '/')) {
+            return $this->redirect($this->generateUrl('view_vendor', array('vendor' => $name, '_format' => $format)));
+        }
+
+        return $this->redirect($this->generateUrl('view_package', array('name' => trim($name, '/'), '_format' => $format)));
+    }
+
+    /**
+     * @Route(
+     *     "/providers/{name}",
+     *     name="view_providers",
+     *     requirements={"name"="[A-Za-z0-9/_.-]+?"},
+     *     defaults={"_format"="html"}
+     * )
+     * @Method({"GET"})
+     */
+    public function viewProvidersAction($name)
+    {
+        /** @var PackageRepository $repo */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
+        $providers = $repo->findProviders($name);
+        if (!$providers) {
+            return $this->redirect($this->generateUrl('search', array('q' => $name, 'reason' => 'package_not_found')));
+        }
+
+        try {
+            $redis = $this->get('snc_redis.default');
+            $trendiness = array();
+            foreach ($providers as $package) {
+                /** @var Package $package */
+                $trendiness[$package->getId()] = (int) $redis->zscore('downloads:trending', $package->getId());
+            }
+            usort($providers, function ($a, $b) use ($trendiness) {
+                if ($trendiness[$a->getId()] === $trendiness[$b->getId()]) {
+                    return strcmp($a->getName(), $b->getName());
+                }
+                return $trendiness[$a->getId()] > $trendiness[$b->getId()] ? -1 : 1;
+            });
+        } catch (ConnectionException $e) {}
+
+        return $this->render('PackagistWebBundle:Package:providers.html.twig', array(
+            'name' => $name,
+            'packages' => $providers,
+            'meta' => $this->getPackagesMetadata($providers),
+            'paginate' => false,
+        ));
+    }
+
+    /**
+     * @Template()
+     * @Route(
+     *     "/packages/{name}.{_format}",
+     *     name="view_package",
+     *     requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "_format"="(json)"},
+     *     defaults={"_format"="html"}
+     * )
+     * @Method({"GET"})
+     */
+    public function viewPackageAction(Request $req, $name)
+    {
+        /** @var PackageRepository $repo */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
+
+        try {
+            /** @var Package $package */
+            $package = $repo->findOneByName($name);
+        } catch (NoResultException $e) {
+            if ('json' === $req->getRequestFormat()) {
+                return new JsonResponse(array('status' => 'error', 'message' => 'Package not found'), 404);
+            }
+
+            if ($providers = $repo->findProviders($name)) {
+                return $this->redirect($this->generateUrl('view_providers', array('name' => $name)));
+            }
+
+            return $this->redirect($this->generateUrl('search', array('q' => $name, 'reason' => 'package_not_found')));
+        }
+
+        if ('json' === $req->getRequestFormat()) {
+            $data = $package->toArray();
+
+            try {
+                $data['downloads'] = $this->get('packagist.download_manager')->getDownloads($package);
+                $data['favers'] = $this->get('packagist.favorite_manager')->getFaverCount($package);
+            } catch (ConnectionException $e) {
+                $data['downloads'] = null;
+                $data['favers'] = null;
+            }
+
+            // TODO invalidate cache on update and make the ttl longer
+            $response = new JsonResponse(array('package' => $data));
+            $response->setSharedMaxAge(3600);
+
+            return $response;
+        }
+
+        $version = null;
+        $expandedVersion = null;
+        $versions = $package->getVersions();
+        if (is_object($versions)) {
+            $versions = $versions->toArray();
+        }
+
+        usort($versions, Package::class.'::sortVersions');
+
+        if (count($versions)) {
+            /** @var VersionRepository $versionRepo */
+            $versionRepo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
+            $version = $versionRepo->getFullVersion(reset($versions)->getId());
+
+            $expandedVersion = reset($versions);
+            foreach ($versions as $v) {
+                if (!$v->isDevelopment()) {
+                    $expandedVersion = $v;
+                    break;
+                }
+            }
+            $expandedVersion = $versionRepo->getFullVersion($expandedVersion->getId());
+        }
+
+        $data = array(
+            'package' => $package,
+            'version' => $version,
+            'versions' => $versions,
+            'expandedVersion' => $expandedVersion,
+        );
+
+        try {
+            $data['downloads'] = $this->get('packagist.download_manager')->getDownloads($package);
+
+            if ($this->getUser()) {
+                $data['is_favorite'] = $this->get('packagist.favorite_manager')->isMarked($this->getUser(), $package);
+            }
+        } catch (ConnectionException $e) {
+        }
+
+        $data['dependents'] = $repo->getDependentCount($package->getName());
+
+        if ($maintainerForm = $this->createAddMaintainerForm($package)) {
+            $data['addMaintainerForm'] = $maintainerForm->createView();
+        }
+        if ($removeMaintainerForm = $this->createRemoveMaintainerForm($package)) {
+            $data['removeMaintainerForm'] = $removeMaintainerForm->createView();
+        }
+        if ($deleteForm = $this->createDeletePackageForm($package)) {
+            $data['deleteForm'] = $deleteForm->createView();
+        }
+        if ($this->getUser() && (
+                $this->isGranted('ROLE_DELETE_PACKAGES')
+                || $package->getMaintainers()->contains($this->getUser())
+            )) {
+            $data['deleteVersionCsrfToken'] = $this->get('security.csrf.token_manager')->getToken('delete_version');
+        }
+
+        return $data;
+    }
+
+    /**
+     * @Route(
+     *     "/packages/{name}/downloads.{_format}",
+     *     name="package_downloads_full",
+     *     requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "_format"="(json)"}
+     * )
+     * @Method({"GET"})
+     */
+    public function viewPackageDownloadsAction(Request $req, $name)
+    {
+        /** @var PackageRepository $repo */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
+
+        try {
+            /** @var $package Package */
+            $package = $repo->findOneByName($name);
+        } catch (NoResultException $e) {
+            if ('json' === $req->getRequestFormat()) {
+                return new JsonResponse(array('status' => 'error', 'message' => 'Package not found'), 404);
+            }
+
+            if ($providers = $repo->findProviders($name)) {
+                return $this->redirect($this->generateUrl('view_providers', array('name' => $name)));
+            }
+
+            return $this->redirect($this->generateUrl('search', array('q' => $name, 'reason' => 'package_not_found')));
+        }
+
+        $versions = $package->getVersions();
+        $data = array(
+            'name' => $package->getName(),
+        );
+
+        try {
+            $data['downloads']['total'] = $this->get('packagist.download_manager')->getDownloads($package);
+            $data['favers'] = $this->get('packagist.favorite_manager')->getFaverCount($package);
+        } catch (ConnectionException $e) {
+            $data['downloads']['total'] = null;
+            $data['favers'] = null;
+        }
+
+        foreach ($versions as $version) {
+            try {
+                $data['downloads']['versions'][$version->getVersion()] = $this->get('packagist.download_manager')->getDownloads($package, $version);
+            } catch (ConnectionException $e) {
+                $data['downloads']['versions'][$version->getVersion()] = null;
+            }
+        }
+
+        $response = new Response(json_encode(array('package' => $data)), 200);
+        $response->setSharedMaxAge(3600);
+
+        return $response;
+    }
+
+    /**
+     * @Route(
+     *     "/versions/{versionId}.{_format}",
+     *     name="view_version",
+     *     requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "versionId"="[0-9]+", "_format"="(json)"}
+     * )
+     * @Method({"GET"})
+     */
+    public function viewPackageVersionAction($versionId)
+    {
+        /** @var VersionRepository $repo  */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
+
+        $html = $this->renderView(
+            'PackagistWebBundle:Package:versionDetails.html.twig',
+            array('version' => $repo->getFullVersion($versionId))
+        );
+
+        return new JsonResponse(array('content' => $html));
+    }
+
+    /**
+     * @Route(
+     *     "/versions/{versionId}/delete",
+     *     name="delete_version",
+     *     requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "versionId"="[0-9]+"}
+     * )
+     * @Method({"DELETE"})
+     */
+    public function deletePackageVersionAction(Request $req, $versionId)
+    {
+        /** @var VersionRepository $repo  */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
+
+        /** @var Version $version  */
+        $version = $repo->getFullVersion($versionId);
+        $package = $version->getPackage();
+
+        if (!$package->getMaintainers()->contains($this->getUser()) && !$this->isGranted('ROLE_DELETE_PACKAGES')) {
+            throw new AccessDeniedException;
+        }
+
+        if (!$this->isCsrfTokenValid('delete_version', $req->request->get('_token'))) {
+            throw new AccessDeniedException;
+        }
+
+        $repo->remove($version);
+        $this->getDoctrine()->getManager()->flush();
+        $this->getDoctrine()->getManager()->clear();
+
+        return new Response('', 204);
+    }
+
+    /**
+     * @Route("/packages/{name}", name="update_package", requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"}, defaults={"_format" = "json"})
+     * @Method({"PUT"})
+     */
+    public function updatePackageAction(Request $req, $name)
+    {
+        $doctrine = $this->getDoctrine();
+
+        try {
+            /** @var Package $package */
+            $package = $doctrine
+                ->getRepository('PackagistWebBundle:Package')
+                ->getPackageByName($name);
+        } catch (NoResultException $e) {
+            return new Response(json_encode(array('status' => 'error', 'message' => 'Package not found',)), 404);
+        }
+
+        $username = $req->request->has('username') ?
+            $req->request->get('username') :
+            $req->query->get('username');
+
+        $apiToken = $req->request->has('apiToken') ?
+            $req->request->get('apiToken') :
+            $req->query->get('apiToken');
+
+        $update = $req->request->get('update', $req->query->get('update'));
+        $autoUpdated = $req->request->get('autoUpdated', $req->query->get('autoUpdated'));
+        $updateEqualRefs = $req->request->get('updateAll', $req->query->get('updateAll'));
+        $showOutput = $req->request->get('showOutput', $req->query->get('showOutput', false));
+
+        $user = $this->getUser() ?: $doctrine
+            ->getRepository('PackagistWebBundle:User')
+            ->findOneBy(array('username' => $username, 'apiToken' => $apiToken));
+
+        if (!$user) {
+            return new Response(json_encode(array('status' => 'error', 'message' => 'Invalid credentials',)), 403);
+        }
+
+        if ($package->getMaintainers()->contains($user) || $this->isGranted('ROLE_UPDATE_PACKAGES')) {
+            $req->getSession()->save();
+
+            if (null !== $autoUpdated) {
+                $package->setAutoUpdated((Boolean) $autoUpdated);
+                $doctrine->getManager()->flush();
+            }
+
+            if ($update) {
+                set_time_limit(3600);
+                $updater = $this->get('packagist.package_updater');
+
+                $io = new BufferIO('', OutputInterface::VERBOSITY_VERY_VERBOSE, new HtmlOutputFormatter(Factory::createAdditionalStyles()));
+                $config = Factory::createConfig();
+                $io->loadConfiguration($config);
+                $repository = new VcsRepository(array('url' => $package->getRepository()), $io, $config);
+                $loader = new ValidatingArrayLoader(new ArrayLoader());
+                $repository->setLoader($loader);
+
+                try {
+                    $updater->update($io, $config, $package, $repository, $updateEqualRefs ? Updater::UPDATE_EQUAL_REFS : 0);
+                } catch (\Exception $e) {
+                    return new JsonResponse([
+                        'status' => 'error',
+                        'message' => '['.get_class($e).'] '.$e->getMessage(),
+                        'details' => '<pre>'.$io->getOutput().'</pre>',
+                    ], 400);
+                }
+
+                if ($showOutput) {
+                    return new JsonResponse([
+                        'status' => 'error',
+                        'message' => 'Update successful',
+                        'details' => '<pre>'.$io->getOutput().'</pre>',
+                    ], 400);
+                }
+            }
+
+            return new Response('{"status": "success"}', 202);
+        }
+
+        return new JsonResponse(array('status' => 'error', 'message' => 'Could not find a package that matches this request (does user maintain the package?)',), 404);
+    }
+
+    /**
+     * @Route("/packages/{name}", name="delete_package", requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"})
+     * @Method({"DELETE"})
+     */
+    public function deletePackageAction(Request $req, $name)
+    {
+        $doctrine = $this->getDoctrine();
+
+        try {
+            /** @var Package $package */
+            $package = $doctrine
+                ->getRepository('PackagistWebBundle:Package')
+                ->findOneByName($name);
+        } catch (NoResultException $e) {
+            throw new NotFoundHttpException('The requested package, '.$name.', was not found.');
+        }
+
+        if (!$form = $this->createDeletePackageForm($package)) {
+            throw new AccessDeniedException;
+        }
+        $form->submit($req->request->get('form'));
+        if ($form->isValid()) {
+            $req->getSession()->save();
+
+            /** @var VersionRepository $versionRepo */
+            $versionRepo = $doctrine->getRepository('PackagistWebBundle:Version');
+            foreach ($package->getVersions() as $version) {
+                $versionRepo->remove($version);
+            }
+
+            $packageId = $package->getId();
+            $em = $doctrine->getManager();
+            $em->remove($package);
+            $em->flush();
+
+            // attempt solr cleanup
+            try {
+                /** @var \Solarium_Client $solarium */
+                $solarium = $this->get('solarium.client');
+
+                $update = $solarium->createUpdate();
+                $update->addDeleteById($packageId);
+                $update->addCommit();
+
+                $solarium->update($update);
+            } catch (\Solarium_Client_HttpException $e) {
+            }
+
+            return new Response('', 204);
+        }
+
+        return new Response('Invalid form input', 400);
+    }
+
+    /**
+     * @Template("PackagistWebBundle:Package:viewPackage.html.twig")
+     * @Route("/packages/{name}/maintainers/", name="add_maintainer", requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"})
+     */
+    public function createMaintainerAction(Request $req, $name)
+    {
+        /** @var $package Package */
+        $package = $this->getDoctrine()
+            ->getRepository('PackagistWebBundle:Package')
+            ->findOneByName($name);
+
+        if (!$package) {
+            throw new NotFoundHttpException('The requested package, '.$name.', was not found.');
+        }
+
+        if (!$form = $this->createAddMaintainerForm($package)) {
+            throw new AccessDeniedException('You must be a package\'s maintainer to modify maintainers.');
+        }
+
+        $data = array(
+            'package' => $package,
+            'versions' => null,
+            'expandedVersion' => null,
+            'version' => null,
+            'addMaintainerForm' => $form->createView(),
+            'show_add_maintainer_form' => true,
+        );
+
+        $form->handleRequest($req);
+        if ($form->isValid()) {
+            try {
+                $em = $this->getDoctrine()->getManager();
+                $user = $form->getData()->getUser();
+
+                if (!empty($user)) {
+                    if (!$package->getMaintainers()->contains($user)) {
+                        $package->addMaintainer($user);
+                        $this->get('packagist.package_manager')->notifyNewMaintainer($user, $package);
+                    }
+
+                    $em->persist($package);
+                    $em->flush();
+
+                    $this->get('session')->getFlashBag()->set('success', $user->getUsername().' is now a '.$package->getName().' maintainer.');
+
+                    return new RedirectResponse($this->generateUrl('view_package', array('name' => $package->getName())));
+                }
+                $this->get('session')->getFlashBag()->set('error', 'The user could not be found.');
+            } catch (\Exception $e) {
+                $this->get('logger')->critical($e->getMessage(), array('exception', $e));
+                $this->get('session')->getFlashBag()->set('error', 'The maintainer could not be added.');
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * @Template("PackagistWebBundle:Package:viewPackage.html.twig")
+     * @Route("/packages/{name}/maintainers/delete", name="remove_maintainer", requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"})
+     */
+    public function removeMaintainerAction(Request $req, $name)
+    {
+        /** @var $package Package */
+        $package = $this->getDoctrine()
+            ->getRepository('PackagistWebBundle:Package')
+            ->findOneByName($name);
+
+        if (!$package) {
+            throw new NotFoundHttpException('The requested package, '.$name.', was not found.');
+        }
+        if (!$removeMaintainerForm = $this->createRemoveMaintainerForm($package)) {
+            throw new AccessDeniedException('You must be a package\'s maintainer to modify maintainers.');
+        }
+
+        $data = array(
+            'package' => $package,
+            'versions' => null,
+            'expandedVersion' => null,
+            'version' => null,
+            'removeMaintainerForm' => $removeMaintainerForm->createView(),
+            'show_remove_maintainer_form' => true,
+        );
+
+        $removeMaintainerForm->handleRequest($req);
+        if ($removeMaintainerForm->isValid()) {
+            try {
+                $em = $this->getDoctrine()->getManager();
+                $user = $removeMaintainerForm->getData()->getUser();
+
+                if (!empty($user)) {
+                    if ($package->getMaintainers()->contains($user)) {
+                        $package->getMaintainers()->removeElement($user);
+                    }
+
+                    $em->persist($package);
+                    $em->flush();
+
+                    $this->get('session')->getFlashBag()->set('success', $user->getUsername().' is no longer a '.$package->getName().' maintainer.');
+
+                    return new RedirectResponse($this->generateUrl('view_package', array('name' => $package->getName())));
+                }
+                $this->get('session')->getFlashBag()->set('error', 'The user could not be found.');
+            } catch (\Exception $e) {
+                $this->get('logger')->critical($e->getMessage(), array('exception', $e));
+                $this->get('session')->getFlashBag()->set('error', 'The maintainer could not be removed.');
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * @Template()
+     * @Route(
+     *     "/packages/{name}/edit",
+     *     name="edit_package",
+     *     requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?"}
+     * )
+     */
+    public function editAction(Request $req, Package $package)
+    {
+        if (!$package->getMaintainers()->contains($this->getUser()) && !$this->isGranted('ROLE_EDIT_PACKAGES')) {
+            throw new AccessDeniedException;
+        }
+
+        $form = $this->createFormBuilder($package, array("validation_groups" => array("Update")))
+            ->add('repository', TextType::class)
+            ->setMethod('POST')
+            ->setAction($this->generateUrl('edit_package', ['name' => $package->getName()]))
+            ->getForm();
+
+        $form->handleRequest($req);
+        if ($form->isValid()) {
+            // Force updating of packages once the package is viewed after the redirect.
+            $package->setCrawledAt(null);
+
+            $em = $this->getDoctrine()->getManager();
+            $em->persist($package);
+            $em->flush();
+
+            $this->get("session")->getFlashBag()->set("success", "Changes saved.");
+
+            return $this->redirect(
+                $this->generateUrl("view_package", array("name" => $package->getName()))
+            );
+        }
+
+        return array(
+            "package" => $package, "form" => $form->createView()
+        );
+    }
+
+    /**
+     * @Route(
+     *      "/packages/{name}/abandon",
+     *      name="abandon_package",
+     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?"}
+     * )
+     * @Template()
+     */
+    public function abandonAction(Request $request, Package $package)
+    {
+        if (!$package->getMaintainers()->contains($this->getUser()) && !$this->isGranted('ROLE_EDIT_PACKAGES')) {
+            throw new AccessDeniedException;
+        }
+
+        $form = $this->createForm(AbandonedType::class);
+        $form->handleRequest($request);
+        if ($form->isValid()) {
+            $package->setAbandoned(true);
+            $package->setReplacementPackage(str_replace('https://packagist.org/packages/', '', $form->get('replacement')->getData()));
+            $package->setIndexedAt(null);
+
+            $em = $this->getDoctrine()->getManager();
+            $em->flush();
+
+            return $this->redirect($this->generateUrl('view_package', array('name' => $package->getName())));
+        }
+
+        return array(
+            'package' => $package,
+            'form'    => $form->createView()
+        );
+    }
+
+    /**
+     * @Route(
+     *      "/packages/{name}/unabandon",
+     *      name="unabandon_package",
+     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?"}
+     * )
+     */
+    public function unabandonAction(Package $package)
+    {
+        if (!$package->getMaintainers()->contains($this->getUser()) && !$this->isGranted('ROLE_EDIT_PACKAGES')) {
+            throw new AccessDeniedException;
+        }
+
+        $package->setAbandoned(false);
+        $package->setReplacementPackage(null);
+        $package->setIndexedAt(null);
+
+        $em = $this->getDoctrine()->getManager();
+        $em->flush();
+
+        return $this->redirect($this->generateUrl('view_package', array('name' => $package->getName())));
+    }
+
+    /**
+     * @Route(
+     *      "/packages/{name}/stats.{_format}",
+     *      name="view_package_stats",
+     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "_format"="(json)"},
+     *      defaults={"_format"="html"}
+     * )
+     * @Template()
+     */
+    public function statsAction(Request $req, Package $package)
+    {
+        $versions = $package->getVersions()->toArray();
+        usort($versions, Package::class.'::sortVersions');
+        $date = $this->guessStatsStartDate($package);
+        $data = [
+            'downloads' => $this->get('packagist.download_manager')->getDownloads($package),
+            'versions' => $versions,
+            'average' => $this->guessStatsAverage($date),
+            'date' => $date->format('Y-m-d'),
+        ];
+
+        if ($req->getRequestFormat() === 'json') {
+            $data['versions'] = array_map(function ($version) {
+                /** @var Version $version */
+                return $version->getVersion();
+            }, $data['versions']);
+
+            return new JsonResponse($data);
+        }
+
+        $data['package'] = $package;
+
+        $expandedVersion = reset($versions);
+        foreach ($versions as $v) {
+            /** @var Version $v */
+            if (!$v->isDevelopment()) {
+                $expandedVersion = $v;
+                break;
+            }
+        }
+        $data['expandedId'] = $expandedVersion ? $expandedVersion->getId() : false;
+
+        return $data;
+    }
+
+    /**
+     * @Route(
+     *      "/packages/{name}/dependents",
+     *      name="view_package_dependents",
+     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?"}
+     * )
+     * @Template()
+     */
+    public function dependentsAction(Request $req, $name)
+    {
+        $page = $req->query->get('page', 1);
+
+        /** @var PackageRepository $repo */
+        $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
+        $depCount = $repo->getDependentCount($name);
+        $packages = $repo->getDependents($name, ($page - 1) * 15, 15);
+
+        $paginator = new Pagerfanta(new FixedAdapter($depCount, $packages));
+        $paginator->setMaxPerPage(15);
+        $paginator->setCurrentPage($page, false, true);
+
+        $data['packages'] = $paginator;
+        $data['count'] = $depCount;
+
+        $data['meta'] = $this->getPackagesMetadata($data['packages']);
+        $data['name'] = $name;
+
+        return $data;
+    }
+
+    /**
+     * @Route(
+     *      "/packages/{name}/stats/all.json",
+     *      name="package_stats",
+     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?"}
+     * )
+     */
+    public function overallStatsAction(Request $req, Package $package, Version $version = null)
+    {
+        if ($from = $req->query->get('from')) {
+            $from = new DateTimeImmutable($from);
+        } else {
+            $from = $this->guessStatsStartDate($version ?: $package);
+        }
+        if ($to = $req->query->get('to')) {
+            $to = new DateTimeImmutable($to);
+        } else {
+            $to = new DateTimeImmutable('-2days 00:00:00');
+        }
+        $average = $req->query->get('average', $this->guessStatsAverage($from, $to));
+
+        $datePoints = $this->createDatePoints($from, $to, $average, $package, $version);
+
+        $redis = $this->get('snc_redis.default');
+        if ($average === 'Daily') {
+            $datePoints = array_map(function ($vals) {
+                return $vals[0];
+            }, $datePoints);
+
+            $datePoints = array(
+                'labels' => array_keys($datePoints),
+                'values' => $redis->mget(array_values($datePoints))
+            );
+        } else {
+            $datePoints = array(
+                'labels' => array_keys($datePoints),
+                'values' => array_values(array_map(function ($vals) use ($redis) {
+                    return array_sum($redis->mget(array_values($vals)));
+                }, $datePoints))
+            );
+        }
+
+        $datePoints['average'] = $average;
+
+        if ($average !== 'daily') {
+            $dividers = [
+                'monthly' => 30.41,
+                'weekly' => 7,
+            ];
+            $divider = $dividers[$average];
+            $datePoints['values'] = array_map(function ($val) use ($divider) {
+                return ceil($val / $divider);
+            }, $datePoints['values']);
+        }
+
+        if (empty($datePoints['labels']) && empty($datePoints['values'])) {
+            $datePoints['labels'][] = date('Y-m-d');
+            $datePoints['values'][] = 0;
+        }
+
+        $response = new JsonResponse($datePoints);
+        $response->setSharedMaxAge(1800);
+
+        return $response;
+    }
+
+    /**
+     * @Route(
+     *      "/packages/{name}/stats/{version}.json",
+     *      name="version_stats",
+     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "version"=".+?"}
+     * )
+     */
+    public function versionStatsAction(Request $req, Package $package, $version)
+    {
+        $normalizer = new VersionParser;
+        $normVersion = $normalizer->normalize($version);
+
+        $version = $this->getDoctrine()->getRepository('PackagistWebBundle:Version')->findOneBy([
+            'package' => $package,
+            'normalizedVersion' => $normVersion
+        ]);
+
+        if (!$version) {
+            throw new NotFoundHttpException();
+        }
+
+        return $this->overallStatsAction($req, $package, $version);
+    }
+
+    private function createAddMaintainerForm(Package $package)
+    {
+        if (!$user = $this->getUser()) {
+            return;
+        }
+
+        if ($this->isGranted('ROLE_EDIT_PACKAGES') || $package->getMaintainers()->contains($user)) {
+            $maintainerRequest = new MaintainerRequest();
+            return $this->createForm(AddMaintainerRequestType::class, $maintainerRequest);
+        }
+    }
+
+    private function createRemoveMaintainerForm(Package $package)
+    {
+        if (!($user = $this->getUser()) || 1 == $package->getMaintainers()->count()) {
+            return;
+        }
+
+        if ($this->isGranted('ROLE_EDIT_PACKAGES') || $package->getMaintainers()->contains($user)) {
+            $maintainerRequest = new MaintainerRequest();
+            return $this->createForm(RemoveMaintainerRequestType::class, $maintainerRequest, array(
+                'package' => $package,
+            ));
+        }
+    }
+
+    private function createDeletePackageForm(Package $package)
+    {
+        if (!$user = $this->getUser()) {
+            return;
+        }
+
+        // super admins bypass additional checks
+        if (!$this->isGranted('ROLE_DELETE_PACKAGES')) {
+            // non maintainers can not delete
+            if (!$package->getMaintainers()->contains($user)) {
+                return;
+            }
+
+            try {
+                $downloads = $this->get('packagist.download_manager')->getTotalDownloads($package);
+            } catch (ConnectionException $e) {
+                return;
+            }
+
+            // more than 100 downloads = established package, do not allow deletion by maintainers
+            if ($downloads > 100) {
+                return;
+            }
+        }
+
+        return $this->createFormBuilder(array())->getForm();
+    }
+
+    private function createDatePoints(DateTimeImmutable $from, DateTimeImmutable $to, $average, Package $package, Version $version = null)
+    {
+        $interval = $this->getStatsInterval($average);
+
+        $dateKey = $average === 'monthly' ? 'Ym' : 'Ymd';
+        $dateFormat = $average === 'monthly' ? 'Y-m' : 'Y-m-d';
+        $dateJump = $average === 'monthly' ? '+1month' : '+1day';
+        if ($average === 'monthly') {
+            $to = new DateTimeImmutable('last day of '.$to->format('Y-m'));
+        }
+
+        $nextDataPointLabel = $from->format($dateFormat);
+        $nextDataPoint = $from->modify($interval);
+
+        $datePoints = [];
+        while ($from <= $to) {
+            $datePoints[$nextDataPointLabel][] = 'dl:'.$package->getId().($version ? '-' . $version->getId() : '').':'.$from->format($dateKey);
+
+            $from = $from->modify($dateJump);
+            if ($from >= $nextDataPoint) {
+                $nextDataPointLabel = $from->format($dateFormat);
+                $nextDataPoint = $from->modify($interval);
+            }
+        }
+
+        return $datePoints;
+    }
+
+    private function guessStatsStartDate($packageOrVersion)
+    {
+        if ($packageOrVersion instanceof Package) {
+            $date = DateTimeImmutable::createFromMutable($packageOrVersion->getCreatedAt());
+        } elseif ($packageOrVersion instanceof Version) {
+            $date = DateTimeImmutable::createFromMutable($packageOrVersion->getReleasedAt());
+        } else {
+            throw new \LogicException('Version or Package expected');
+        }
+
+        $statsRecordDate = new DateTimeImmutable('2012-04-13 00:00:00');
+        if ($date < $statsRecordDate) {
+            $date = $statsRecordDate;
+        }
+
+        return $date->setTime(0, 0, 0);
+    }
+
+    private function guessStatsAverage(DateTimeImmutable $from, DateTimeImmutable $to = null)
+    {
+        if ($to === null) {
+            $to = new DateTimeImmutable('-2 days');
+        }
+        if ($from < $to->modify('-48months')) {
+            $average = 'monthly';
+        } elseif ($from < $to->modify('-7months')) {
+            $average = 'weekly';
+        } else {
+            $average = 'daily';
+        }
+
+        return $average;
+    }
+
+    private function getStatsInterval($average)
+    {
+        $intervals = [
+            'monthly' => '+1month',
+            'weekly' => '+7days',
+            'daily' => '+1day',
+        ];
+
+        if (!isset($intervals[$average])) {
+            throw new BadRequestHttpException();
+        }
+
+        return $intervals[$average];
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/UserController.php b/src/Packagist/WebBundle/Controller/UserController.php
new file mode 100644
index 0000000..ca5f7a1
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/UserController.php
@@ -0,0 +1,181 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Doctrine\ORM\NoResultException;
+use FOS\UserBundle\Model\UserInterface;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\User;
+use Packagist\WebBundle\Model\RedisAdapter;
+use Pagerfanta\Adapter\DoctrineORMAdapter;
+use Pagerfanta\Pagerfanta;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class UserController extends Controller
+{
+    /**
+     * @Template()
+     * @Route("/users/{name}/packages/", name="user_packages")
+     * @ParamConverter("user", options={"mapping": {"name": "username"}})
+     */
+    public function packagesAction(Request $req, User $user)
+    {
+        $packages = $this->getUserPackages($req, $user);
+
+        return array(
+            'packages' => $packages,
+            'meta' => $this->getPackagesMetadata($packages),
+            'user' => $user,
+        );
+    }
+
+    /**
+     * @param Request $req
+     * @return Response
+     */
+    public function myProfileAction(Request $req)
+    {
+        $user = $this->container->get('security.token_storage')->getToken()->getUser();
+        if (!is_object($user) || !$user instanceof UserInterface) {
+            throw new AccessDeniedException('This user does not have access to this section.');
+        }
+
+        $packages = $this->getUserPackages($req, $user);
+
+        return $this->container->get('templating')->renderResponse(
+            'FOSUserBundle:Profile:show.html.twig',
+            array(
+                'packages' => $packages,
+                'meta' => $this->getPackagesMetadata($packages),
+                'user' => $user,
+            )
+        );
+    }
+
+
+    /**
+     * @Template()
+     * @Route("/users/{name}/", name="user_profile")
+     * @ParamConverter("user", options={"mapping": {"name": "username"}})
+     */
+    public function profileAction(Request $req, User $user)
+    {
+        $packages = $this->getUserPackages($req, $user);
+
+        return array(
+            'packages' => $packages,
+            'meta' => $this->getPackagesMetadata($packages),
+            'user' => $user,
+        );
+    }
+
+    /**
+     * @Template()
+     * @Route("/users/{name}/favorites/", name="user_favorites")
+     * @ParamConverter("user", options={"mapping": {"name": "username"}})
+     * @Method({"GET"})
+     */
+    public function favoritesAction(Request $req, User $user)
+    {
+        try {
+            if (!$this->get('snc_redis.default')->isConnected()) {
+                $this->get('snc_redis.default')->connect();
+            }
+        } catch (\Exception $e) {
+            $this->get('session')->getFlashBag()->set('error', 'Could not connect to the Redis database.');
+            $this->get('logger')->notice($e->getMessage(), array('exception' => $e));
+
+            return array('user' => $user, 'packages' => array());
+        }
+
+        $paginator = new Pagerfanta(
+            new RedisAdapter($this->get('packagist.favorite_manager'), $user, 'getFavorites', 'getFavoriteCount')
+        );
+
+        $paginator->setMaxPerPage(15);
+        $paginator->setCurrentPage($req->query->get('page', 1), false, true);
+
+        return array('packages' => $paginator, 'user' => $user);
+    }
+
+    /**
+     * @Route("/users/{name}/favorites/", name="user_add_fav", defaults={"_format" = "json"})
+     * @ParamConverter("user", options={"mapping": {"name": "username"}})
+     * @Method({"POST"})
+     */
+    public function postFavoriteAction(Request $req, User $user)
+    {
+        if ($user->getId() !== $this->getUser()->getId()) {
+            throw new AccessDeniedException('You can only change your own favorites');
+        }
+
+        $package = $req->request->get('package');
+        try {
+            $package = $this->getDoctrine()
+                ->getRepository('PackagistWebBundle:Package')
+                ->findOneByName($package);
+        } catch (NoResultException $e) {
+            throw new NotFoundHttpException('The given package "'.$package.'" was not found.');
+        }
+
+        $this->get('packagist.favorite_manager')->markFavorite($user, $package);
+
+        return new Response('{"status": "success"}', 201);
+    }
+
+    /**
+     * @Route("/users/{name}/favorites/{package}", name="user_remove_fav", defaults={"_format" = "json"}, requirements={"package"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?"})
+     * @ParamConverter("user", options={"mapping": {"name": "username"}})
+     * @ParamConverter("package", options={"mapping": {"package": "name"}})
+     * @Method({"DELETE"})
+     */
+    public function deleteFavoriteAction(User $user, Package $package)
+    {
+        if ($user->getId() !== $this->getUser()->getId()) {
+            throw new AccessDeniedException('You can only change your own favorites');
+        }
+
+        $this->get('packagist.favorite_manager')->removeFavorite($user, $package);
+
+        return new Response('{"status": "success"}', 204);
+    }
+
+    /**
+     * @param Request $req
+     * @param User $user
+     * @return Pagerfanta
+     */
+    protected function getUserPackages($req, $user)
+    {
+        $packages = $this->getDoctrine()
+            ->getRepository('PackagistWebBundle:Package')
+            ->getFilteredQueryBuilder(array('maintainer' => $user->getId()))
+            ->orderBy('p.name');
+
+        $paginator = new Pagerfanta(new DoctrineORMAdapter($packages, true));
+        $paginator->setMaxPerPage(15);
+        $paginator->setCurrentPage($req->query->get('page', 1), false, true);
+
+        return $paginator;
+    }
+}
diff --git a/src/Packagist/WebBundle/Controller/WebController.php b/src/Packagist/WebBundle/Controller/WebController.php
new file mode 100644
index 0000000..839cbf4
--- /dev/null
+++ b/src/Packagist/WebBundle/Controller/WebController.php
@@ -0,0 +1,481 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Controller;
+
+use Packagist\WebBundle\Form\Model\SearchQuery;
+use Packagist\WebBundle\Form\Type\SearchQueryType;
+use Pagerfanta\Adapter\SolariumAdapter;
+use Pagerfanta\Pagerfanta;
+use Predis\Connection\ConnectionException;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class WebController extends Controller
+{
+    /**
+     * @Template()
+     * @Route("/", name="home")
+     */
+    public function indexAction()
+    {
+        return array('page' => 'home');
+    }
+
+    /**
+     * Rendered by views/Web/searchSection.html.twig
+     */
+    public function searchFormAction(Request $req)
+    {
+        $form = $this->createForm(SearchQueryType::class, new SearchQuery(), [
+            'action' => $this->generateUrl('search.ajax'),
+        ]);
+
+        $filteredOrderBys = $this->getFilteredOrderedBys($req);
+        $normalizedOrderBys = $this->getNormalizedOrderBys($filteredOrderBys);
+
+        $this->computeSearchQuery($req, $filteredOrderBys);
+
+        $form->handleRequest($req);
+
+        $orderBysViewModel = $this->getOrderBysViewModel($req, $normalizedOrderBys);
+        return $this->render('PackagistWebBundle:Web:searchForm.html.twig', array(
+            'searchForm' => $form->createView(),
+            'orderBys' => $orderBysViewModel
+        ));
+    }
+
+    /**
+     * @Route("/search/", name="search.ajax")
+     * @Route("/search.{_format}", requirements={"_format"="(html|json)"}, name="search", defaults={"_format"="html"})
+     * @Method({"GET"})
+     */
+    public function searchAction(Request $req)
+    {
+        $form = $this->createForm(SearchQueryType::class, new SearchQuery());
+
+        $filteredOrderBys = $this->getFilteredOrderedBys($req);
+        $normalizedOrderBys = $this->getNormalizedOrderBys($filteredOrderBys);
+
+        $this->computeSearchQuery($req, $filteredOrderBys);
+
+        $typeFilter = $req->query->get('type');
+        $tagsFilter = $req->query->get('tags');
+
+        if ($req->query->has('search_query') || $typeFilter || $tagsFilter) {
+            /** @var $solarium \Solarium_Client */
+            $solarium = $this->get('solarium.client');
+            $select = $solarium->createSelect();
+
+            // configure dismax
+            $dismax = $select->getDisMax();
+            $dismax->setQueryFields(array('name^4', 'description', 'tags', 'text', 'text_ngram', 'name_split^2'));
+            $dismax->setPhraseFields(array('description'));
+            $dismax->setBoostFunctions(array('log(trendiness)^10'));
+            $dismax->setMinimumMatch(1);
+            $dismax->setQueryParser('edismax');
+
+            // filter by type
+            if ($typeFilter) {
+                $filterQueryTerm = sprintf('type:"%s"', $select->getHelper()->escapeTerm($typeFilter));
+                $filterQuery = $select->createFilterQuery('type')->setQuery($filterQueryTerm);
+                $select->addFilterQuery($filterQuery);
+            }
+
+            // filter by tags
+            if ($tagsFilter) {
+                $tags = array();
+                foreach ((array) $tagsFilter as $tag) {
+                    $tags[] = $select->getHelper()->escapeTerm($tag);
+                }
+                $filterQueryTerm = sprintf('tags:("%s")', implode('" AND "', $tags));
+                $filterQuery = $select->createFilterQuery('tags')->setQuery($filterQueryTerm);
+                $select->addFilterQuery($filterQuery);
+            }
+
+            if (!empty($filteredOrderBys)) {
+                $select->addSorts($normalizedOrderBys);
+            }
+
+            $form->handleRequest($req);
+            if ($form->isValid()) {
+                $escapedQuery = $select->getHelper()->escapeTerm($form->getData()->getQuery());
+                $escapedQuery = preg_replace('/(^| )\\\\-(\S)/', '$1-$2', $escapedQuery);
+                $escapedQuery = preg_replace('/(^| )\\\\\+(\S)/', '$1+$2', $escapedQuery);
+                if ((substr_count($escapedQuery, '"') % 2) == 0) {
+                    $escapedQuery = str_replace('\\"', '"', $escapedQuery);
+                }
+                $select->setQuery($escapedQuery);
+            }
+
+            $paginator = new Pagerfanta(new SolariumAdapter($solarium, $select));
+
+            $perPage = $req->query->getInt('per_page', 15);
+            if ($perPage <= 0 || $perPage > 100) {
+                if ($req->getRequestFormat() === 'json') {
+                    return JsonResponse::create(array(
+                        'status' => 'error',
+                        'message' => 'The optional packages per_page parameter must be an integer between 1 and 100 (default: 15)',
+                    ), 400)->setCallback($req->query->get('callback'));
+                }
+
+                $perPage = max(0, min(100, $perPage));
+            }
+            $paginator->setMaxPerPage($perPage);
+
+            $paginator->setCurrentPage($req->query->get('page', 1), false, true);
+
+            $metadata = array();
+
+            foreach ($paginator as $package) {
+                if (is_numeric($package->id)) {
+                    $metadata['downloads'][$package->id] = $package->downloads;
+                    $metadata['favers'][$package->id] = $package->favers;
+                }
+            }
+
+            if ($req->getRequestFormat() === 'json') {
+                try {
+                    $result = array(
+                        'results' => array(),
+                        'total' => $paginator->getNbResults(),
+                    );
+                } catch (\Solarium_Client_HttpException $e) {
+                    return JsonResponse::create(array(
+                        'status' => 'error',
+                        'message' => 'Could not connect to the search server',
+                    ), 500)->setCallback($req->query->get('callback'));
+                }
+
+                foreach ($paginator as $package) {
+                    if (ctype_digit((string) $package->id)) {
+                        $url = $this->generateUrl('view_package', array('name' => $package->name), UrlGeneratorInterface::ABSOLUTE_URL);
+                    } else {
+                        $url = $this->generateUrl('view_providers', array('name' => $package->name), UrlGeneratorInterface::ABSOLUTE_URL);
+                    }
+
+                    $row = array(
+                        'name' => $package->name,
+                        'description' => $package->description ?: '',
+                        'url' => $url,
+                        'repository' => $package->repository,
+                    );
+                    if (is_numeric($package->id)) {
+                        $row['downloads'] = $metadata['downloads'][$package->id];
+                        $row['favers'] = $metadata['favers'][$package->id];
+                    } else {
+                        $row['virtual'] = true;
+                    }
+                    $result['results'][] = $row;
+                }
+
+                if ($paginator->hasNextPage()) {
+                    $params = array(
+                        '_format' => 'json',
+                        'q' => $form->getData()->getQuery(),
+                        'page' => $paginator->getNextPage()
+                    );
+                    if ($tagsFilter) {
+                        $params['tags'] = (array) $tagsFilter;
+                    }
+                    if ($typeFilter) {
+                        $params['type'] = $typeFilter;
+                    }
+                    if ($perPage !== 15) {
+                        $params['per_page'] = $perPage;
+                    }
+                    $result['next'] = $this->generateUrl('search', $params, UrlGeneratorInterface::ABSOLUTE_URL);
+                }
+
+                return JsonResponse::create($result)->setCallback($req->query->get('callback'));
+            }
+
+            if ($req->isXmlHttpRequest()) {
+                try {
+                    return $this->render('PackagistWebBundle:Web:search.html.twig', array(
+                        'packages' => $paginator,
+                        'meta' => $metadata,
+                        'noLayout' => true,
+                    ));
+                } catch (\Twig_Error_Runtime $e) {
+                    if (!$e->getPrevious() instanceof \Solarium_Client_HttpException) {
+                        throw $e;
+                    }
+                    return JsonResponse::create(array(
+                        'status' => 'error',
+                        'message' => 'Could not connect to the search server',
+                    ), 500)->setCallback($req->query->get('callback'));
+                }
+            }
+
+            return $this->render('PackagistWebBundle:Web:search.html.twig', array(
+                'packages' => $paginator,
+                'meta' => $metadata,
+            ));
+        } elseif ($req->getRequestFormat() === 'json') {
+            return JsonResponse::create(array(
+                'error' => 'Missing search query, example: ?q=example'
+            ), 400)->setCallback($req->query->get('callback'));
+        }
+
+        return $this->render('PackagistWebBundle:Web:search.html.twig');
+    }
+
+    /**
+     * @Route("/statistics", name="stats")
+     * @Template
+     */
+    public function statsAction()
+    {
+        $packages = $this->getDoctrine()
+            ->getConnection()
+            ->fetchAll('SELECT COUNT(*) count, DATE_FORMAT(createdAt, "%Y-%m") month FROM `package` GROUP BY month');
+
+        $versions = $this->getDoctrine()
+            ->getConnection()
+            ->fetchAll('SELECT COUNT(*) count, DATE_FORMAT(releasedAt, "%Y-%m") month FROM `package_version` GROUP BY month');
+
+        $chart = array('versions' => array(), 'packages' => array(), 'months' => array());
+
+        // prepare x axis
+        $date = new \DateTime($packages[0]['month'].'-01');
+        $now = new \DateTime;
+        while ($date < $now) {
+            $chart['months'][] = $month = $date->format('Y-m');
+            $date->modify('+1month');
+        }
+
+        // prepare data
+        $count = 0;
+        foreach ($packages as $dataPoint) {
+            $count += $dataPoint['count'];
+            $chart['packages'][$dataPoint['month']] = $count;
+        }
+
+        $count = 0;
+        foreach ($versions as $dataPoint) {
+            $count += $dataPoint['count'];
+            if (in_array($dataPoint['month'], $chart['months'])) {
+                $chart['versions'][$dataPoint['month']] = $count;
+            }
+        }
+
+        // fill gaps at the end of the chart
+        if (count($chart['months']) > count($chart['packages'])) {
+            $chart['packages'] += array_fill(0, count($chart['months']) - count($chart['packages']), !empty($chart['packages']) ? max($chart['packages']) : 0);
+        }
+        if (count($chart['months']) > count($chart['versions'])) {
+            $chart['versions'] += array_fill(0, count($chart['months']) - count($chart['versions']), !empty($chart['versions']) ? max($chart['versions']) : 0);
+        }
+
+        $res = $this->getDoctrine()
+            ->getConnection()
+            ->fetchAssoc('SELECT DATE_FORMAT(createdAt, "%Y-%m-%d") createdAt FROM `package` ORDER BY id LIMIT 1');
+        $downloadsStartDate = $res['createdAt'] > '2012-04-13' ? $res['createdAt'] : '2012-04-13';
+
+        try {
+            $redis = $this->get('snc_redis.default');
+            $downloads = $redis->get('downloads') ?: 0;
+
+            $date = new \DateTime($downloadsStartDate.' 00:00:00');
+            $yesterday = new \DateTime('-2days 00:00:00');
+            $dailyGraphStart = new \DateTime('-32days 00:00:00'); // 30 days before yesterday
+
+            $dlChart = $dlChartMonthly = array();
+            while ($date <= $yesterday) {
+                if ($date > $dailyGraphStart) {
+                    $dlChart[$date->format('Y-m-d')] = 'downloads:'.$date->format('Ymd');
+                }
+                $dlChartMonthly[$date->format('Y-m')] = 'downloads:'.$date->format('Ym');
+                $date->modify('+1day');
+            }
+
+            $dlChart = array(
+                'labels' => array_keys($dlChart),
+                'values' => $redis->mget(array_values($dlChart))
+            );
+            $dlChartMonthly = array(
+                'labels' => array_keys($dlChartMonthly),
+                'values' => $redis->mget(array_values($dlChartMonthly))
+            );
+        } catch (ConnectionException $e) {
+            $downloads = 'N/A';
+            $dlChart = $dlChartMonthly = null;
+        }
+
+        return array(
+            'chart' => $chart,
+            'packages' => !empty($chart['packages']) ? max($chart['packages']) : 0,
+            'versions' => !empty($chart['versions']) ? max($chart['versions']) : 0,
+            'downloads' => $downloads,
+            'downloadsChart' => $dlChart,
+            'maxDailyDownloads' => !empty($dlChart) ? max($dlChart['values']) : null,
+            'downloadsChartMonthly' => $dlChartMonthly,
+            'maxMonthlyDownloads' => !empty($dlChartMonthly) ? max($dlChartMonthly['values']) : null,
+            'downloadsStartDate' => $downloadsStartDate,
+        );
+    }
+
+    /**
+     * @param Request $req
+     *
+     * @return array
+     */
+    protected function getFilteredOrderedBys(Request $req)
+    {
+        $orderBys = $req->query->get('orderBys', array());
+        if (!$orderBys) {
+            $orderBys = $req->query->get('search_query');
+            $orderBys = isset($orderBys['orderBys']) ? $orderBys['orderBys'] : array();
+        }
+
+        if ($orderBys) {
+            $allowedSorts = array(
+                'downloads' => 1,
+                'favers' => 1
+            );
+
+            $allowedOrders = array(
+                'asc' => 1,
+                'desc' => 1,
+            );
+
+            $filteredOrderBys = array();
+
+            foreach ($orderBys as $orderBy) {
+                if (isset($orderBy['sort'])
+                    && isset($allowedSorts[$orderBy['sort']])
+                    && isset($orderBy['order'])
+                    && isset($allowedOrders[$orderBy['order']])) {
+                    $filteredOrderBys[] = $orderBy;
+                }
+            }
+        } else {
+            $filteredOrderBys = array();
+        }
+
+        return $filteredOrderBys;
+    }
+
+    /**
+     * @param array $orderBys
+     *
+     * @return array
+     */
+    protected function getNormalizedOrderBys(array $orderBys)
+    {
+        $normalizedOrderBys = array();
+
+        foreach ($orderBys as $sort) {
+            $normalizedOrderBys[$sort['sort']] = $sort['order'];
+        }
+
+        return $normalizedOrderBys;
+    }
+
+    /**
+     * @param Request $req
+     * @param array $normalizedOrderBys
+     *
+     * @return array
+     */
+    protected function getOrderBysViewModel(Request $req, array $normalizedOrderBys)
+    {
+        $makeDefaultArrow = function ($sort) use ($normalizedOrderBys) {
+            if (isset($normalizedOrderBys[$sort])) {
+                if (strtolower($normalizedOrderBys[$sort]) === 'asc') {
+                    $val = 'glyphicon-arrow-up';
+                } else {
+                    $val = 'glyphicon-arrow-down';
+                }
+            } else {
+                $val = '';
+            }
+
+            return $val;
+        };
+
+        $makeDefaultHref = function ($sort) use ($req, $normalizedOrderBys) {
+            if (isset($normalizedOrderBys[$sort])) {
+                if (strtolower($normalizedOrderBys[$sort]) === 'asc') {
+                    $order = 'desc';
+                } else {
+                    $order = 'asc';
+                }
+            } else {
+                $order = 'desc';
+            }
+
+            $query = $req->query->get('search_query');
+            $query = isset($query['query']) ? $query['query'] : '';
+
+            return '?' . http_build_query(array(
+                'q' => $query,
+                'orderBys' => array(
+                    array(
+                        'sort' => $sort,
+                        'order' => $order
+                    )
+                )
+            ));
+        };
+
+        return array(
+            'downloads' => array(
+                'title' => 'Sort by downloads',
+                'class' => 'glyphicon-arrow-down',
+                'arrowClass' => $makeDefaultArrow('downloads'),
+                'href' => $makeDefaultHref('downloads')
+            ),
+            'favers' => array(
+                'title' => 'Sort by favorites',
+                'class' => 'glyphicon-star',
+                'arrowClass' => $makeDefaultArrow('favers'),
+                'href' => $makeDefaultHref('favers')
+            ),
+        );
+    }
+
+    /**
+     * @param Request $req
+     * @param array $filteredOrderBys
+     */
+    private function computeSearchQuery(Request $req, array $filteredOrderBys)
+    {
+        // transform q=search shortcut
+        if ($req->query->has('q') || $req->query->has('orderBys')) {
+            $searchQuery = array();
+
+            $q = $req->query->get('q');
+
+            if ($q !== null) {
+                $searchQuery['query'] = $q;
+            }
+
+            if (!empty($filteredOrderBys)) {
+                $searchQuery['orderBys'] = $filteredOrderBys;
+            }
+
+            $req->query->set(
+                'search_query',
+                $searchQuery
+            );
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/DependencyInjection/Configuration.php b/src/Packagist/WebBundle/DependencyInjection/Configuration.php
new file mode 100644
index 0000000..4474482
--- /dev/null
+++ b/src/Packagist/WebBundle/DependencyInjection/Configuration.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Packagist\WebBundle\DependencyInjection;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+/**
+ * This is the class that validates and merges configuration from your app/config files
+ *
+ * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
+ */
+class Configuration implements ConfigurationInterface
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function getConfigTreeBuilder()
+    {
+        $treeBuilder = new TreeBuilder();
+        $rootNode = $treeBuilder->root('packagist_web');
+
+        $rootNode
+            ->children()
+                ->scalarNode('rss_max_items')->defaultValue(40)->end()
+            ->end();
+
+        return $treeBuilder;
+    }
+}
diff --git a/src/Packagist/WebBundle/DependencyInjection/PackagistWebExtension.php b/src/Packagist/WebBundle/DependencyInjection/PackagistWebExtension.php
new file mode 100644
index 0000000..53429c8
--- /dev/null
+++ b/src/Packagist/WebBundle/DependencyInjection/PackagistWebExtension.php
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\DependencyInjection;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class PackagistWebExtension extends Extension
+{
+    public function load(array $configs, ContainerBuilder $container)
+    {
+        $configuration = new Configuration();
+        $config = $this->processConfiguration($configuration, $configs);
+
+        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+        $loader->load('services.yml');
+
+        $container->setParameter('packagist_web.rss_max_items', $config['rss_max_items']);
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/Author.php b/src/Packagist/WebBundle/Entity/Author.php
new file mode 100644
index 0000000..7173215
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/Author.php
@@ -0,0 +1,269 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity(repositoryClass="Packagist\WebBundle\Entity\AuthorRepository")
+ * @ORM\Table(name="author")
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Author
+{
+    /**
+     * @ORM\Id
+     * @ORM\Column(type="integer")
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    private $id;
+
+    /**
+     * Unique package name
+     *
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $name;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $email;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $homepage;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $role;
+
+    /**
+     * @ORM\ManyToMany(targetEntity="Packagist\WebBundle\Entity\Version", mappedBy="authors")
+     */
+    private $versions;
+
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\User", inversedBy="authors")
+     */
+    private $owner;
+
+    /**
+     * @ORM\Column(type="datetime")
+     */
+    private $createdAt;
+
+    /**
+     * @ORM\Column(type="datetime", nullable=true)
+     */
+    private $updatedAt;
+
+    public function __construct()
+    {
+        $this->versions = new ArrayCollection();
+        $this->createdAt = new \DateTime;
+    }
+
+    public function toArray()
+    {
+        $data = array();
+
+        if ($this->getName()) {
+            $data['name'] = $this->getName();
+        }
+        if ($this->getEmail()) {
+            $data['email'] = $this->getEmail();
+        }
+        if ($this->getHomepage()) {
+            $data['homepage'] = $this->getHomepage();
+        }
+        if ($this->getRole()) {
+            $data['role'] = $this->getRole();
+        }
+
+        return $data;
+    }
+
+    /**
+     * Get id
+     *
+     * @return string $id
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set name
+     *
+     * @param string $name
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * Get name
+     *
+     * @return string $name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Set createdAt
+     *
+     * @param \DateTime $createdAt
+     */
+    public function setCreatedAt($createdAt)
+    {
+        $this->createdAt = $createdAt;
+    }
+
+    /**
+     * Get createdAt
+     *
+     * @return \DateTime $createdAt
+     */
+    public function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
+
+    /**
+     * Add versions
+     *
+     * @param Version $version
+     */
+    public function addVersion(Version $version)
+    {
+        $this->versions[] = $version;
+    }
+
+    /**
+     * Get versions
+     *
+     * @return Version[]
+     */
+    public function getVersions()
+    {
+        return $this->versions;
+    }
+
+    /**
+     * Set updatedAt
+     *
+     * @param \DateTime $updatedAt
+     */
+    public function setUpdatedAt(\DateTime $updatedAt)
+    {
+        $this->updatedAt = $updatedAt;
+    }
+
+    /**
+     * Get updatedAt
+     *
+     * @return \DateTime $updatedAt
+     */
+    public function getUpdatedAt()
+    {
+        return $this->updatedAt;
+    }
+
+    /**
+     * Set email
+     *
+     * @param string $email
+     */
+    public function setEmail($email)
+    {
+        $this->email = $email;
+    }
+
+    /**
+     * Get email
+     *
+     * @return string
+     */
+    public function getEmail()
+    {
+        return $this->email;
+    }
+
+    /**
+     * Set homepage
+     *
+     * @param string $homepage
+     */
+    public function setHomepage($homepage)
+    {
+        $this->homepage = $homepage;
+    }
+
+    /**
+     * Get homepage
+     *
+     * @return string
+     */
+    public function getHomepage()
+    {
+        return $this->homepage;
+    }
+
+    /**
+     * Set role
+     *
+     * @param string $role
+     */
+    public function setRole($role)
+    {
+        $this->role = $role;
+    }
+
+    /**
+     * Get role
+     *
+     * @return string $role
+     */
+    public function getRole()
+    {
+        return $this->role;
+    }
+
+    /**
+     * Set owner
+     *
+     * @param User $owner
+     */
+    public function setOwner(User $owner)
+    {
+        $this->owner = $owner;
+    }
+
+    /**
+     * Get owner
+     *
+     * @return User
+     */
+    public function getOwner()
+    {
+        return $this->owner;
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/AuthorRepository.php b/src/Packagist/WebBundle/Entity/AuthorRepository.php
new file mode 100644
index 0000000..de7a9c2
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/AuthorRepository.php
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\EntityRepository;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class AuthorRepository extends EntityRepository
+{
+    public function findOneByNameAndPackage($author, Package $package)
+    {
+        $qb = $this->createQueryBuilder('a');
+        $qb->select('a')
+            ->leftJoin('a.versions', 'v')
+            ->leftJoin('v.package', 'p')
+            ->where('p.id = :packageId')
+            ->andWhere('a.name = :author')
+            ->setMaxResults(1)
+            ->setParameters(array('author' => $author, 'packageId' => $package->getId()));
+
+        return $qb->getQuery()->getOneOrNullResult();
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/ConflictLink.php b/src/Packagist/WebBundle/Entity/ConflictLink.php
new file mode 100644
index 0000000..afc325f
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/ConflictLink.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(name="link_conflict")
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ConflictLink extends PackageLink
+{
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\Version", inversedBy="conflict")
+     */
+    protected $version;
+}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Entity/DevRequireLink.php b/src/Packagist/WebBundle/Entity/DevRequireLink.php
new file mode 100644
index 0000000..c01879b
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/DevRequireLink.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(name="link_require_dev", indexes={
+ *     @ORM\Index(name="link_require_dev_package_name_idx",columns={"version_id", "packageName"})
+ * })
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class DevRequireLink extends PackageLink
+{
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\Version", inversedBy="devRequire")
+     */
+    protected $version;
+}
diff --git a/src/Packagist/WebBundle/Entity/Package.php b/src/Packagist/WebBundle/Entity/Package.php
new file mode 100644
index 0000000..c78575b
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/Package.php
@@ -0,0 +1,831 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Composer\Factory;
+use Composer\IO\NullIO;
+use Composer\Repository\VcsRepository;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
+use Drupal\ParseComposer\Repository as DrupalRepository;
+use Composer\Repository\RepositoryManager;
+use Composer\Repository\Vcs\GitHubDriver;
+
+/**
+ * @ORM\Entity(repositoryClass="Packagist\WebBundle\Entity\PackageRepository")
+ * @ORM\Table(
+ *     name="package",
+ *     uniqueConstraints={@ORM\UniqueConstraint(name="package_name_idx", columns={"name"})},
+ *     indexes={
+ *         @ORM\Index(name="indexed_idx",columns={"indexedAt"}),
+ *         @ORM\Index(name="crawled_idx",columns={"crawledAt"}),
+ *         @ORM\Index(name="dumped_idx",columns={"dumpedAt"})
+ *     }
+ * )
+ * @Assert\Callback(callback="isPackageUnique")
+ * @Assert\Callback(callback="isVendorWritable")
+ * @Assert\Callback(callback="isRepositoryValid", groups={"Update", "Default"})
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Package
+{
+    /**
+     * @ORM\Id
+     * @ORM\Column(type="integer")
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    private $id;
+
+    /**
+     * Unique package name
+     *
+     * @ORM\Column(length=191)
+     */
+    private $name;
+
+    /**
+     * @ORM\Column(nullable=true)
+     */
+    private $type;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $description;
+
+    /**
+     * @ORM\Column(type="string", nullable=true)
+     */
+    private $language;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $readme;
+
+    /**
+     * @ORM\Column(type="integer", nullable=true, name="github_stars")
+     */
+    private $gitHubStars;
+
+    /**
+     * @ORM\Column(type="integer", nullable=true, name="github_watches")
+     */
+    private $gitHubWatches;
+
+    /**
+     * @ORM\Column(type="integer", nullable=true, name="github_forks")
+     */
+    private $gitHubForks;
+
+    /**
+     * @ORM\Column(type="integer", nullable=true, name="github_open_issues")
+     */
+    private $gitHubOpenIssues;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\Version", mappedBy="package")
+     */
+    private $versions;
+
+    /**
+     * @ORM\ManyToMany(targetEntity="User", inversedBy="packages")
+     * @ORM\JoinTable(name="maintainers_packages")
+     */
+    private $maintainers;
+
+    /**
+     * @ORM\Column()
+     * @Assert\NotBlank(groups={"Update", "Default"})
+     */
+    private $repository;
+
+    // dist-tags / rel or runtime?
+
+    /**
+     * @ORM\Column(type="datetime")
+     */
+    private $createdAt;
+
+    /**
+     * @ORM\Column(type="datetime", nullable=true)
+     */
+    private $updatedAt;
+
+    /**
+     * @ORM\Column(type="datetime", nullable=true)
+     */
+    private $crawledAt;
+
+    /**
+     * @ORM\Column(type="datetime", nullable=true)
+     */
+    private $indexedAt;
+
+    /**
+     * @ORM\Column(type="datetime", nullable=true)
+     */
+    private $dumpedAt;
+
+    /**
+     * @ORM\Column(type="boolean")
+     */
+    private $autoUpdated = false;
+
+    /**
+     * @var bool
+     * @ORM\Column(type="boolean")
+     */
+    private $abandoned = false;
+
+    /**
+     * @var string
+     * @ORM\Column(type="string", length=255, nullable=true)
+     */
+    private $replacementPackage;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default"=false})
+     */
+    private $updateFailureNotified = false;
+
+    private $entityRepository;
+    private $router;
+
+    /**
+     * @var \Composer\Repository\Vcs\VcsDriverInterface
+     */
+    private $vcsDriver = true;
+    private $vcsDriverError;
+
+    /**
+     * @var array lookup table for versions
+     */
+    private $cachedVersions;
+
+    public function __construct()
+    {
+        $this->versions = new ArrayCollection();
+        $this->createdAt = new \DateTime;
+    }
+
+    public function toArray()
+    {
+        $versions = array();
+        foreach ($this->getVersions() as $version) {
+            /** @var $version Version */
+            $versions[$version->getVersion()] = $version->toArray();
+        }
+        $maintainers = array();
+        foreach ($this->getMaintainers() as $maintainer) {
+            /** @var $maintainer User */
+            $maintainers[] = $maintainer->toArray();
+        }
+        $data = array(
+            'name' => $this->getName(),
+            'description' => $this->getDescription(),
+            'time' => $this->getCreatedAt()->format('c'),
+            'maintainers' => $maintainers,
+            'versions' => $versions,
+            'type' => $this->getType(),
+            'repository' => $this->getRepository(),
+        );
+
+        if ($this->isAbandoned()) {
+            $data['abandoned'] = $this->getReplacementPackage() ?: true;
+        }
+
+        return $data;
+    }
+
+    public function isRepositoryValid(ExecutionContextInterface $context)
+    {
+        // vcs driver was not nulled which means the repository was not set/modified and is still valid
+        if (true === $this->vcsDriver && null !== $this->getName()) {
+            return;
+        }
+
+        $property = 'repository';
+        $driver = $this->vcsDriver;
+        if (!is_object($driver)) {
+            if (preg_match('{https?://.+@}', $this->repository)) {
+                $context->buildViolation('URLs with user@host are not supported, use a read-only public URL')
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+            } elseif (is_string($this->vcsDriverError)) {
+                $context->buildViolation('Uncaught Exception: '.$this->vcsDriverError)
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+            } else {
+                $context->buildViolation('No valid/supported repository was found at the given URL')
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+            }
+            return;
+        }
+        try {
+            $information = $driver->getComposerInformation($driver->getRootIdentifier());
+
+            if (false === $information) {
+                $context->buildViolation('No composer.json was found in the '.$driver->getRootIdentifier().' branch.')
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+                return;
+            }
+
+            if (empty($information['name'])) {
+                $context->buildViolation('The package name was not found in the composer.json, make sure there is a name present.')
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+                return;
+            }
+
+            if (!preg_match('{^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9]([_.-]?[a-z0-9]+)*$}i', $information['name'])) {
+                $context->buildViolation('The package name '.$information['name'].' is invalid, it should have a vendor name, a forward slash, and a package name. The vendor and package name can be words separated by -, . or _. The complete name should match "[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9]([_.-]?[a-z0-9]+)*".')
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+                return;
+            }
+
+            if (preg_match('{\.json$}', $information['name'])) {
+                $context->buildViolation('The package name '.$information['name'].' is invalid, package names can not end in .json, consider renaming it or perhaps using a -json suffix instead.')
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+                return;
+            }
+
+            if (preg_match('{[A-Z]}', $information['name'])) {
+                $suggestName = preg_replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $information['name']);
+                $suggestName = strtolower($suggestName);
+
+                $context->buildViolation('The package name '.$information['name'].' is invalid, it should not contain uppercase characters. We suggest using '.$suggestName.' instead.')
+                    ->atPath($property)
+                    ->addViolation()
+                ;
+                return;
+            }
+        } catch (\Exception $e) {
+            $context->buildViolation('We had problems parsing your composer.json file, the parser reports: '.$e->getMessage())
+                ->atPath($property)
+                ->addViolation()
+            ;
+        }
+        if (null === $this->getName()) {
+            $context->buildViolation('An unexpected error has made our parser fail to find a package name in your repository, if you think this is incorrect please try again')
+                ->atPath($property)
+                ->addViolation()
+            ;
+        }
+    }
+
+    public function setEntityRepository($repository)
+    {
+        $this->entityRepository = $repository;
+    }
+
+    public function setRouter($router)
+    {
+        $this->router = $router;
+    }
+
+    public function isPackageUnique(ExecutionContextInterface $context)
+    {
+        try {
+            if ($this->entityRepository->findOneByName($this->name)) {
+                $context->buildViolation('A package with the name <a href="'.$this->router->generate('view_package', array('name' => $this->name)).'">'.$this->name.'</a> already exists.')
+                    ->atPath('repository')
+                    ->addViolation()
+                ;
+            }
+        } catch (\Doctrine\ORM\NoResultException $e) {}
+    }
+
+    public function isVendorWritable(ExecutionContextInterface $context)
+    {
+        try {
+            $vendor = $this->getVendor();
+            if ($vendor && $this->entityRepository->isVendorTaken($vendor, reset($this->maintainers))) {
+                $context->buildViolation('The vendor is already taken by someone else. '
+                        . 'You may ask them to add your package and give you maintainership access. '
+                        . 'The packages already in that vendor namespace can be found at '
+                        . '<a href="'.$this->router->generate('view_vendor', array('vendor' => $vendor)).'">'.$vendor.'</a>')
+                    ->atPath('repository')
+                    ->addViolation()
+                ;
+            }
+        } catch (\Doctrine\ORM\NoResultException $e) {}
+    }
+
+    /**
+     * Get id
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set name
+     *
+     * @param string $name
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * Get name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Get vendor prefix
+     *
+     * @return string
+     */
+    public function getVendor()
+    {
+        return preg_replace('{/.*$}', '', $this->name);
+    }
+
+    /**
+     * Get package name without vendor
+     *
+     * @return string
+     */
+    public function getPackageName()
+    {
+        return preg_replace('{^[^/]*/}', '', $this->name);
+    }
+
+    /**
+     * Set description
+     *
+     * @param string $description
+     */
+    public function setDescription($description)
+    {
+        $this->description = $description;
+    }
+
+    /**
+     * Get description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    /**
+     * Set language
+     *
+     * @param string $language
+     */
+    public function setLanguage($language)
+    {
+        $this->language = $language;
+    }
+
+    /**
+     * Get language
+     *
+     * @return string
+     */
+    public function getLanguage()
+    {
+        return $this->language;
+    }
+
+    /**
+     * Set readme
+     *
+     * @param string $readme
+     */
+    public function setReadme($readme)
+    {
+        $this->readme = $readme;
+    }
+
+    /**
+     * Get readme
+     *
+     * @return string
+     */
+    public function getReadme()
+    {
+        return $this->readme;
+    }
+
+    /**
+     * @param int $val
+     */
+    public function setGitHubStars($val)
+    {
+        $this->gitHubStars = $val;
+    }
+
+    /**
+     * @return int
+     */
+    public function getGitHubStars()
+    {
+        return $this->gitHubStars;
+    }
+
+    /**
+     * @param int $val
+     */
+    public function setGitHubWatches($val)
+    {
+        $this->gitHubWatches = $val;
+    }
+
+    /**
+     * @return int
+     */
+    public function getGitHubWatches()
+    {
+        return $this->gitHubWatches;
+    }
+
+    /**
+     * @param int $val
+     */
+    public function setGitHubForks($val)
+    {
+        $this->gitHubForks = $val;
+    }
+
+    /**
+     * @return int
+     */
+    public function getGitHubForks()
+    {
+        return $this->gitHubForks;
+    }
+
+    /**
+     * @param int $val
+     */
+    public function setGitHubOpenIssues($val)
+    {
+        $this->gitHubOpenIssues = $val;
+    }
+
+    /**
+     * @return int
+     */
+    public function getGitHubOpenIssues()
+    {
+        return $this->gitHubOpenIssues;
+    }
+
+    /**
+     * Set createdAt
+     *
+     * @param \DateTime $createdAt
+     */
+    public function setCreatedAt($createdAt)
+    {
+        $this->createdAt = $createdAt;
+    }
+
+    /**
+     * Get createdAt
+     *
+     * @return \DateTime
+     */
+    public function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
+
+    /**
+     * Set repository
+     *
+     * @param string $repository
+     */
+    public function setRepository($repoUrl)
+    {
+        $this->vcsDriver = null;
+
+        // prevent local filesystem URLs
+        if (preg_match('{^(\.|[a-z]:|/)}i', $repoUrl)) {
+            return;
+        }
+
+        $repoUrl = preg_replace('{^git@github.com:}i', 'https://github.com/', $repoUrl);
+        $this->repository = $repoUrl;
+
+        // avoid user@host URLs
+        if (preg_match('{https?://.+@}', $repoUrl)) {
+            return;
+        }
+
+        try {
+            $io = new NullIO();
+            $config = Factory::createConfig();
+            $io->loadConfiguration($config);
+            $repository = new DrupalRepository(array('url' => $this->repository), $io, $config);
+
+            $driver = $this->vcsDriver = $repository->getDriver();
+            if (!$driver) {
+                return;
+            }
+            $information = $driver->getComposerInformation($driver->getRootIdentifier());
+            if (!isset($information['name'])) {
+                return;
+            }
+            if (null === $this->getName()) {
+                $this->setName($information['name']);
+            }
+            if ($driver instanceof GitHubDriver) {
+                $this->repository = $driver->getRepositoryUrl();
+            }
+        } catch (\Exception $e) {
+            $this->vcsDriverError = '['.get_class($e).'] '.$e->getMessage();
+        }
+    }
+
+    /**
+     * Get repository
+     *
+     * @return string $repository
+     */
+    public function getRepository()
+    {
+        return $this->repository;
+    }
+
+    /**
+     * Add versions
+     *
+     * @param Version $versions
+     */
+    public function addVersions(Version $versions)
+    {
+        $this->versions[] = $versions;
+    }
+
+    /**
+     * Get versions
+     *
+     * @return \Doctrine\Common\Collections\Collection
+     */
+    public function getVersions()
+    {
+        return $this->versions;
+    }
+
+    public function getVersion($normalizedVersion)
+    {
+        if (null === $this->cachedVersions) {
+            $this->cachedVersions = array();
+            foreach ($this->getVersions() as $version) {
+                $this->cachedVersions[strtolower($version->getNormalizedVersion())] = $version;
+            }
+        }
+
+        if (isset($this->cachedVersions[strtolower($normalizedVersion)])) {
+            return $this->cachedVersions[strtolower($normalizedVersion)];
+        }
+    }
+
+    /**
+     * Set updatedAt
+     *
+     * @param \DateTime $updatedAt
+     */
+    public function setUpdatedAt($updatedAt)
+    {
+        $this->updatedAt = $updatedAt;
+        $this->setUpdateFailureNotified(false);
+    }
+
+    /**
+     * Get updatedAt
+     *
+     * @return \DateTime
+     */
+    public function getUpdatedAt()
+    {
+        return $this->updatedAt;
+    }
+
+    /**
+     * Set crawledAt
+     *
+     * @param \DateTime|null $crawledAt
+     */
+    public function setCrawledAt($crawledAt)
+    {
+        $this->crawledAt = $crawledAt;
+    }
+
+    /**
+     * Get crawledAt
+     *
+     * @return \DateTime
+     */
+    public function getCrawledAt()
+    {
+        return $this->crawledAt;
+    }
+
+    /**
+     * Set indexedAt
+     *
+     * @param \DateTime $indexedAt
+     */
+    public function setIndexedAt($indexedAt)
+    {
+        $this->indexedAt = $indexedAt;
+    }
+
+    /**
+     * Get indexedAt
+     *
+     * @return \DateTime
+     */
+    public function getIndexedAt()
+    {
+        return $this->indexedAt;
+    }
+
+    /**
+     * Set dumpedAt
+     *
+     * @param \DateTime $dumpedAt
+     */
+    public function setDumpedAt($dumpedAt)
+    {
+        $this->dumpedAt = $dumpedAt;
+    }
+
+    /**
+     * Get dumpedAt
+     *
+     * @return \DateTime
+     */
+    public function getDumpedAt()
+    {
+        return $this->dumpedAt;
+    }
+
+    /**
+     * Add maintainers
+     *
+     * @param User $maintainer
+     */
+    public function addMaintainer(User $maintainer)
+    {
+        $this->maintainers[] = $maintainer;
+    }
+
+    /**
+     * Get maintainers
+     *
+     * @return \Doctrine\Common\Collections\Collection
+     */
+    public function getMaintainers()
+    {
+        return $this->maintainers;
+    }
+
+    /**
+     * Set type
+     *
+     * @param string $type
+     */
+    public function setType($type)
+    {
+        $this->type = $type;
+    }
+
+    /**
+     * Get type
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Set autoUpdated
+     *
+     * @param Boolean $autoUpdated
+     */
+    public function setAutoUpdated($autoUpdated)
+    {
+        $this->autoUpdated = $autoUpdated;
+    }
+
+    /**
+     * Get autoUpdated
+     *
+     * @return Boolean
+     */
+    public function isAutoUpdated()
+    {
+        return $this->autoUpdated;
+    }
+
+    /**
+     * Set updateFailureNotified
+     *
+     * @param Boolean $updateFailureNotified
+     */
+    public function setUpdateFailureNotified($updateFailureNotified)
+    {
+        $this->updateFailureNotified = $updateFailureNotified;
+    }
+
+    /**
+     * Get updateFailureNotified
+     *
+     * @return Boolean
+     */
+    public function isUpdateFailureNotified()
+    {
+        return $this->updateFailureNotified;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isAbandoned()
+    {
+        return $this->abandoned;
+    }
+
+    /**
+     * @param boolean $abandoned
+     */
+    public function setAbandoned($abandoned)
+    {
+        $this->abandoned = $abandoned;
+    }
+
+    /**
+     * @return string
+     */
+    public function getReplacementPackage()
+    {
+        return $this->replacementPackage;
+    }
+
+    /**
+     * @param string $replacementPackage
+     */
+    public function setReplacementPackage($replacementPackage)
+    {
+        $this->replacementPackage = $replacementPackage;
+    }
+
+    public static function sortVersions($a, $b)
+    {
+        $aVersion = $a->getNormalizedVersion();
+        $bVersion = $b->getNormalizedVersion();
+        $aVersion = preg_replace('{^dev-.*}', '0.0.0-alpha', $aVersion);
+        $bVersion = preg_replace('{^dev-.*}', '0.0.0-alpha', $bVersion);
+
+        // equal versions are sorted by date
+        if ($aVersion === $bVersion) {
+            return $b->getReleasedAt() > $a->getReleasedAt() ? 1 : -1;
+        }
+
+        // the rest is sorted by version
+        return version_compare($bVersion, $aVersion);
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/PackageLink.php b/src/Packagist/WebBundle/Entity/PackageLink.php
new file mode 100644
index 0000000..7b0af73
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/PackageLink.php
@@ -0,0 +1,125 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\MappedSuperclass()
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+abstract class PackageLink
+{
+    /**
+     * @ORM\Id
+     * @ORM\Column(type="integer")
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    private $id;
+
+    /**
+     * @ORM\Column(length=191)
+     */
+    private $packageName;
+
+    /**
+     * @ORM\Column(type="text")
+     */
+    private $packageVersion;
+
+    /**
+     * Base property holding the version - this must remain protected since it
+     * is redefined with an annotation in the child class
+     */
+    protected $version;
+
+    public function toArray()
+    {
+        return array($this->getPackageName() => $this->getPackageVersion());
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set packageName
+     *
+     * @param string $packageName
+     */
+    public function setPackageName($packageName)
+    {
+        $this->packageName = $packageName;
+    }
+
+    /**
+     * Get packageName
+     *
+     * @return string
+     */
+    public function getPackageName()
+    {
+        return $this->packageName;
+    }
+
+    /**
+     * Set packageVersion
+     *
+     * @param string $packageVersion
+     */
+    public function setPackageVersion($packageVersion)
+    {
+        $this->packageVersion = $packageVersion;
+    }
+
+    /**
+     * Get packageVersion
+     *
+     * @return string
+     */
+    public function getPackageVersion()
+    {
+        return $this->packageVersion;
+    }
+
+    /**
+     * Set version
+     *
+     * @param Version $version
+     */
+    public function setVersion(Version $version)
+    {
+        $this->version = $version;
+    }
+
+    /**
+     * Get version
+     *
+     * @return Version
+     */
+    public function getVersion()
+    {
+        return $this->version;
+    }
+
+    public function __toString()
+    {
+        return $this->packageName.' '.$this->packageVersion;
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/PackageRepository.php b/src/Packagist/WebBundle/Entity/PackageRepository.php
new file mode 100644
index 0000000..fa2c604
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/PackageRepository.php
@@ -0,0 +1,414 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\EntityRepository;
+use Doctrine\ORM\QueryBuilder;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class PackageRepository extends EntityRepository
+{
+    /**
+     * Lists all package names array(name => true)
+     *
+     * @var array
+     */
+    private $packageNames;
+
+    /**
+     * Lists all provided names array(name => true)
+     *
+     * @var array
+     */
+    private $providedNames;
+
+    public function packageExists($name)
+    {
+        $packages = $this->getRawPackageNames();
+
+        return isset($packages[$name]) || in_array(strtolower($name), $packages, true);
+    }
+
+    public function packageIsProvided($name)
+    {
+        $packages = $this->getProvidedNames();
+
+        return isset($packages[$name]) || in_array(strtolower($name), $packages, true);
+    }
+
+    public function getPackageNames($fields = array())
+    {
+        return array_keys($this->getRawPackageNames());
+    }
+
+    public function getRawPackageNames()
+    {
+        if (null !== $this->packageNames) {
+            return $this->packageNames;
+        }
+
+        $names = null;
+        $apc = extension_loaded('apcu');
+
+        if ($apc) {
+            $names = apcu_fetch('packagist_package_names');
+        }
+
+        if (!is_array($names)) {
+            $query = $this->getEntityManager()
+                ->createQuery("SELECT p.name FROM Packagist\WebBundle\Entity\Package p");
+
+            $names = $this->getPackageNamesForQuery($query);
+            $names = array_combine($names, array_map('strtolower', $names));
+            if ($apc) {
+                apcu_store('packagist_package_names', $names, 3600);
+            }
+        }
+
+        return $this->packageNames = $names;
+    }
+
+    public function getProvidedNames()
+    {
+        if (null !== $this->providedNames) {
+            return $this->providedNames;
+        }
+
+        $names = null;
+        $apc = extension_loaded('apcu');
+
+        // TODO use container to set caching key and ttl
+        if ($apc) {
+            $names = apcu_fetch('packagist_provided_names');
+        }
+
+        if (!is_array($names)) {
+            $query = $this->getEntityManager()
+                ->createQuery("SELECT p.packageName AS name FROM Packagist\WebBundle\Entity\ProvideLink p GROUP BY p.packageName");
+
+            $names = $this->getPackageNamesForQuery($query);
+            $names = array_combine($names, array_map('strtolower', $names));
+            if ($apc) {
+                apcu_store('packagist_provided_names', $names, 3600);
+            }
+        }
+
+        return $this->providedNames = $names;
+    }
+
+    public function findProviders($name)
+    {
+        $query = $this->createQueryBuilder('p')
+            ->select('p')
+            ->leftJoin('p.versions', 'pv')
+            ->leftJoin('pv.provide', 'pr')
+            ->where('pv.development = true')
+            ->andWhere('pr.packageName = :name')
+            ->orderBy('p.name')
+            ->getQuery()
+            ->setParameters(array('name' => $name));
+
+        return $query->getResult();
+    }
+
+    public function getPackageNamesByType($type)
+    {
+        $query = $this->getEntityManager()
+            ->createQuery("SELECT p.name FROM Packagist\WebBundle\Entity\Package p WHERE p.type = :type")
+            ->setParameters(array('type' => $type));
+
+        return $this->getPackageNamesForQuery($query);
+    }
+
+    public function getPackageNamesByVendor($vendor)
+    {
+        $query = $this->getEntityManager()
+            ->createQuery("SELECT p.name FROM Packagist\WebBundle\Entity\Package p WHERE p.name LIKE :vendor")
+            ->setParameters(array('vendor' => $vendor.'/%'));
+
+        return $this->getPackageNamesForQuery($query);
+    }
+
+    public function getPackagesWithFields($filters, $fields)
+    {
+        $selector = '';
+        foreach ($fields as $field) {
+            $selector .= ', p.'.$field;
+        }
+        $where = '';
+        foreach ($filters as $filter => $val) {
+            $where .= 'p.'.$filter.' = :'.$filter;
+        }
+        if ($where) {
+            $where = 'WHERE '.$where;
+        }
+        $query = $this->getEntityManager()
+            ->createQuery("SELECT p.name $selector  FROM Packagist\WebBundle\Entity\Package p $where")
+            ->setParameters($filters);
+
+        $result = array();
+        foreach ($query->getScalarResult() as $row) {
+            $name = $row['name'];
+            unset($row['name']);
+            $result[$name] = $row;
+        }
+
+        return $result;
+    }
+
+    private function getPackageNamesForQuery($query)
+    {
+        $names = array();
+        foreach ($query->getScalarResult() as $row) {
+            $names[] = $row['name'];
+        }
+
+        if (defined('SORT_FLAG_CASE')) {
+            sort($names, SORT_STRING | SORT_FLAG_CASE);
+        } else {
+            sort($names, SORT_STRING);
+        }
+
+        return $names;
+    }
+
+    public function getStalePackages()
+    {
+        $conn = $this->getEntityManager()->getConnection();
+
+        return $conn->fetchAll(
+            'SELECT p.id FROM package p
+            WHERE p.abandoned = false
+            AND (
+                p.crawledAt IS NULL
+                OR (p.autoUpdated = 0 AND p.crawledAt < :crawled)
+                OR (p.crawledAt < :autocrawled)
+            )
+            ORDER BY p.id ASC',
+            array(
+                'crawled' => date('Y-m-d H:i:s', strtotime('-1week')),
+                'autocrawled' => date('Y-m-d H:i:s', strtotime('-1month')),
+            )
+        );
+    }
+
+    public function getStalePackagesForIndexing()
+    {
+        $conn = $this->getEntityManager()->getConnection();
+
+        return $conn->fetchAll('SELECT p.id FROM package p WHERE p.indexedAt IS NULL OR p.indexedAt <= p.crawledAt ORDER BY p.id ASC');
+    }
+
+    public function getStalePackagesForDumping()
+    {
+        $conn = $this->getEntityManager()->getConnection();
+
+        return $conn->fetchAll('SELECT p.id FROM package p WHERE p.dumpedAt IS NULL OR p.dumpedAt <= p.crawledAt  ORDER BY p.id ASC');
+    }
+
+    public function findOneByName($name)
+    {
+        $qb = $this->getBaseQueryBuilder()
+            ->where('p.name = ?0')
+            ->setParameters(array($name));
+        return $qb->getQuery()->getSingleResult();
+    }
+
+    public function getPackageByName($name)
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('p', 'm')
+            ->from('Packagist\WebBundle\Entity\Package', 'p')
+            ->leftJoin('p.maintainers', 'm')
+            ->where('p.name = ?0')
+            ->setParameters(array($name));
+
+        return $qb->getQuery()->getSingleResult();
+    }
+
+    public function getPackagesWithVersions(array $ids = null, $filters = array())
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('p', 'v')
+            ->from('Packagist\WebBundle\Entity\Package', 'p')
+            ->leftJoin('p.versions', 'v')
+            ->orderBy('v.development', 'DESC')
+            ->addOrderBy('v.releasedAt', 'DESC');
+
+        if (null !== $ids) {
+            $qb->where($qb->expr()->in('p.id', ':ids'))
+                ->setParameter('ids', $ids);
+        }
+
+        $this->addFilters($qb, $filters);
+
+        return $qb->getQuery()->getResult();
+    }
+
+    public function getGitHubStars(array $ids)
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('p.gitHubStars', 'p.id')
+            ->from('Packagist\WebBundle\Entity\Package', 'p')
+            ->where($qb->expr()->in('p.id', ':ids'))
+            ->setParameter('ids', $ids);
+
+        return $qb->getQuery()->getResult();
+    }
+
+    public function getFilteredQueryBuilder(array $filters = array())
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('p')
+            ->from('Packagist\WebBundle\Entity\Package', 'p');
+
+        if (isset($filters['tag'])) {
+            $qb->leftJoin('p.versions', 'v');
+            $qb->leftJoin('v.tags', 't');
+        }
+
+        $qb->orderBy('p.id', 'DESC');
+
+        $this->addFilters($qb, $filters);
+
+        return $qb;
+    }
+
+    public function isVendorTaken($vendor, User $user)
+    {
+        $query = $this->getEntityManager()
+            ->createQuery(
+                "SELECT p.name, m.id user_id
+                FROM Packagist\WebBundle\Entity\Package p
+                JOIN p.maintainers m
+                WHERE p.name LIKE :vendor")
+            ->setParameters(array('vendor' => $vendor.'/%'));
+
+        $rows = $query->getArrayResult();
+        if (!$rows) {
+            return false;
+        }
+
+        foreach ($rows as $row) {
+            if ($row['user_id'] === $user->getId()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public function getDependentCount($name)
+    {
+        $apc = extension_loaded('apcu');
+
+        if ($apc) {
+            $count = apcu_fetch('packagist_dependentsCount_'.$name);
+        }
+
+        if (!isset($count) || !is_numeric($count)) {
+            $count = $this->getEntityManager()->getConnection()->fetchColumn(
+                "SELECT COUNT(DISTINCT v.package_id)
+                FROM package_version v
+                LEFT JOIN link_require r ON v.id = r.version_id AND r.packageName = :name
+                LEFT JOIN link_require_dev rd ON v.id = rd.version_id AND rd.packageName = :name
+                WHERE v.development AND (r.packageName IS NOT NULL OR rd.packageName IS NOT NULL)",
+                ['name' => $name]
+            );
+
+            if ($apc) {
+                apcu_store('packagist_dependentsCount_'.$name, $count, 7*86400);
+            }
+        }
+
+        return (int) $count;
+    }
+
+    public function getDependents($name, $offset = 0, $limit = 15)
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('p.id, p.name, p.description, p.language, p.abandoned, p.replacementPackage')
+            ->from('Packagist\WebBundle\Entity\Package', 'p')
+            ->join('p.versions', 'v')
+            ->leftJoin('v.devRequire', 'dr')
+            ->leftJoin('v.require', 'r')
+            ->where('v.development = true')
+            ->andWhere('(r.packageName = :name OR dr.packageName = :name)')
+            ->groupBy('p.id, p.name, p.description, p.language, p.abandoned, p.replacementPackage')
+            ->orderBy('p.name')
+            ->setParameter('name', $name);
+
+        return $qb->getQuery()
+            ->setMaxResults($limit)
+            ->setFirstResult($offset)
+            ->useResultCache(true, 7*86400, 'dependents_'.$name.'_'.$offset.'_'.$limit)
+            ->getResult();
+    }
+
+    private function addFilters(QueryBuilder $qb, array $filters)
+    {
+        foreach ($filters as $name => $value) {
+            if (null === $value) {
+                continue;
+            }
+
+            switch ($name) {
+                case 'tag':
+                    $qb->andWhere($qb->expr()->in('t.name', ':'.$name));
+                    break;
+
+                case 'maintainer':
+                    $qb->leftJoin('p.maintainers', 'm');
+                    $qb->andWhere($qb->expr()->in('m.id', ':'.$name));
+                    break;
+
+                default:
+                    $qb->andWhere($qb->expr()->in('p.'.$name, ':'.$name));
+                    break;
+            }
+
+            $qb->setParameter($name, $value);
+        }
+    }
+
+    public function getBaseQueryBuilder()
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('p', 'v', 't', 'm')
+            ->from('Packagist\WebBundle\Entity\Package', 'p')
+            ->leftJoin('p.versions', 'v')
+            ->leftJoin('p.maintainers', 'm')
+            ->leftJoin('v.tags', 't')
+            ->orderBy('v.development', 'DESC')
+            ->addOrderBy('v.releasedAt', 'DESC');
+
+        return $qb;
+    }
+
+    /**
+     * Gets the most recent packages created
+     *
+     * @return QueryBuilder
+     */
+    public function getQueryBuilderForNewestPackages()
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('p')
+            ->from('Packagist\WebBundle\Entity\Package', 'p')
+            ->orderBy('p.id', 'DESC');
+
+        return $qb;
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/ProvideLink.php b/src/Packagist/WebBundle/Entity/ProvideLink.php
new file mode 100644
index 0000000..f6df323
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/ProvideLink.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(name="link_provide")
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ProvideLink extends PackageLink
+{
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\Version", inversedBy="provide")
+     */
+    protected $version;
+}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Entity/ReplaceLink.php b/src/Packagist/WebBundle/Entity/ReplaceLink.php
new file mode 100644
index 0000000..747d4d8
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/ReplaceLink.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(name="link_replace")
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ReplaceLink extends PackageLink
+{
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\Version", inversedBy="replace")
+     */
+    protected $version;
+}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Entity/RequireLink.php b/src/Packagist/WebBundle/Entity/RequireLink.php
new file mode 100644
index 0000000..9a498ff
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/RequireLink.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(name="link_require", indexes={
+ *     @ORM\Index(name="link_require_package_name_idx",columns={"version_id", "packageName"})
+ * })
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class RequireLink extends PackageLink
+{
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\Version", inversedBy="require")
+     */
+    protected $version;
+}
diff --git a/src/Packagist/WebBundle/Entity/SuggestLink.php b/src/Packagist/WebBundle/Entity/SuggestLink.php
new file mode 100644
index 0000000..534e87c
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/SuggestLink.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(name="link_suggest")
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class SuggestLink extends PackageLink
+{
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\Version", inversedBy="suggest")
+     */
+    protected $version;
+}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Entity/Tag.php b/src/Packagist/WebBundle/Entity/Tag.php
new file mode 100644
index 0000000..92440e9
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/Tag.php
@@ -0,0 +1,126 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(
+ *     name="tag",
+ *     uniqueConstraints={@ORM\UniqueConstraint(name="tag_name_idx", columns={"name"})}
+ * )
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Tag
+{
+    /**
+     * @ORM\Id
+     * @ORM\Column(type="integer")
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    private $id;
+
+    /**
+     * @ORM\Column(length=191)
+     * @Assert\NotBlank()
+     */
+    private $name;
+
+    /**
+     * @ORM\ManyToMany(targetEntity="Packagist\WebBundle\Entity\Version", mappedBy="tags")
+     */
+    private $versions;
+
+    public function __construct($name = null)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * @param \Doctrine\ORM\EntityManager $em
+     * @param string                      $name
+     * @param bool                        $create
+     *
+     * @return Tag
+     * @throws \Doctrine\ORM\NoResultException
+     */
+    public static function getByName(EntityManager $em, $name, $create = false)
+    {
+        try {
+            $qb = $em->createQueryBuilder();
+            $qb->select('t')
+                ->from(__CLASS__, 't')
+                ->where('t.name = ?1')
+                ->setMaxResults(1)
+                ->setParameter(1, $name);
+
+            return $qb->getQuery()->getSingleResult();
+        } catch (\Doctrine\ORM\NoResultException $e) {
+            if ($create) {
+                $tag = new self($name);
+                $em->persist($tag);
+
+                return $tag;
+            }
+            throw $e;
+        }
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Add versions
+     *
+     * @param Version $versions
+     */
+    public function addVersions(Version $versions)
+    {
+        $this->versions[] = $versions;
+    }
+
+    /**
+     * Get versions
+     *
+     * @return Version[]
+     */
+    public function getVersions()
+    {
+        return $this->versions;
+    }
+
+    public function __toString()
+    {
+        return (string) $this->name;
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/User.php b/src/Packagist/WebBundle/Entity/User.php
new file mode 100644
index 0000000..4d5dd26
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/User.php
@@ -0,0 +1,267 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\Mapping as ORM;
+use FOS\UserBundle\Model\User as BaseUser;
+
+/**
+ * @ORM\Entity(repositoryClass="Packagist\WebBundle\Entity\UserRepository")
+ * @ORM\Table(name="fos_user")
+ * @ORM\AttributeOverrides({
+ *     @ORM\AttributeOverride(name="username",
+ *         column=@ORM\Column(
+ *             name="username",
+ *             type="string",
+ *             length=191
+ *         )
+ *     ),
+ *     @ORM\AttributeOverride(name="usernameCanonical",
+ *         column=@ORM\Column(
+ *             name="username_canonical",
+ *             type="string",
+ *             length=191,
+ *             unique=true
+ *         )
+ *     ),
+ *     @ORM\AttributeOverride(name="email",
+ *         column=@ORM\Column(
+ *             name="email",
+ *             type="string",
+ *             length=191
+ *         )
+ *     ),
+ *     @ORM\AttributeOverride(name="emailCanonical",
+ *         column=@ORM\Column(
+ *             name="email_canonical",
+ *             type="string",
+ *             length=191,
+ *             unique=true
+ *         )
+ *     )
+ * })
+ */
+class User extends BaseUser
+{
+    /**
+     * @ORM\Id
+     * @ORM\Column(type="integer")
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    protected $id;
+
+    /**
+     * @ORM\ManyToMany(targetEntity="Package", mappedBy="maintainers")
+     */
+    private $packages;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\Author", mappedBy="owner")
+     */
+    private $authors;
+
+    /**
+     * @ORM\Column(type="datetime")
+     */
+    private $createdAt;
+
+    /**
+     * @ORM\Column(type="string", length=20, nullable=true)
+     * @var string
+     */
+    private $apiToken;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     * @var string
+     */
+    private $githubId;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     * @var string
+     */
+    private $githubToken;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default"=true})
+     * @var string
+     */
+    private $failureNotifications = true;
+
+    public function __construct()
+    {
+        $this->packages = new ArrayCollection();
+        $this->authors = new ArrayCollection();
+        $this->createdAt = new \DateTime();
+        parent::__construct();
+    }
+
+    public function toArray()
+    {
+        return array(
+            'name' => $this->getUsername(),
+        );
+    }
+
+    /**
+     * Add packages
+     *
+     * @param Package $packages
+     */
+    public function addPackages(Package $packages)
+    {
+        $this->packages[] = $packages;
+    }
+
+    /**
+     * Get packages
+     *
+     * @return Package[]
+     */
+    public function getPackages()
+    {
+        return $this->packages;
+    }
+
+    /**
+     * Add authors
+     *
+     * @param Author $authors
+     */
+    public function addAuthors(Author $authors)
+    {
+        $this->authors[] = $authors;
+    }
+
+    /**
+     * Get authors
+     *
+     * @return Author[]
+     */
+    public function getAuthors()
+    {
+        return $this->authors;
+    }
+
+    /**
+     * Set createdAt
+     *
+     * @param \DateTime $createdAt
+     */
+    public function setCreatedAt($createdAt)
+    {
+        $this->createdAt = $createdAt;
+    }
+
+    /**
+     * Get createdAt
+     *
+     * @return \DateTime
+     */
+    public function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
+
+    /**
+     * Set apiToken
+     *
+     * @param string $apiToken
+     */
+    public function setApiToken($apiToken)
+    {
+        $this->apiToken = $apiToken;
+    }
+
+    /**
+     * Get apiToken
+     *
+     * @return string
+     */
+    public function getApiToken()
+    {
+        return $this->apiToken;
+    }
+
+    /**
+     * Get githubId.
+     *
+     * @return string
+     */
+    public function getGithubId()
+    {
+        return $this->githubId;
+    }
+
+    /**
+     * Set githubId.
+     *
+     * @param string $githubId
+     */
+    public function setGithubId($githubId)
+    {
+        $this->githubId = $githubId;
+    }
+
+    /**
+     * Get githubId.
+     *
+     * @return string
+     */
+    public function getGithubToken()
+    {
+        return $this->githubToken;
+    }
+
+    /**
+     * Set githubToken.
+     *
+     * @param string $githubToken
+     */
+    public function setGithubToken($githubToken)
+    {
+        $this->githubToken = $githubToken;
+    }
+
+    /**
+     * Set failureNotifications
+     *
+     * @param Boolean $failureNotifications
+     */
+    public function setFailureNotifications($failureNotifications)
+    {
+        $this->failureNotifications = $failureNotifications;
+    }
+
+    /**
+     * Get failureNotifications
+     *
+     * @return Boolean
+     */
+    public function getFailureNotifications()
+    {
+        return $this->failureNotifications;
+    }
+
+    /**
+     * Get failureNotifications
+     *
+     * @return Boolean
+     */
+    public function isNotifiableForFailures()
+    {
+        return $this->failureNotifications;
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/UserRepository.php b/src/Packagist/WebBundle/Entity/UserRepository.php
new file mode 100644
index 0000000..38824f1
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/UserRepository.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\EntityRepository;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class UserRepository extends EntityRepository
+{
+    public function findUsersMissingApiToken()
+    {
+        $qb = $this->createQueryBuilder('u')
+            ->where('u.apiToken IS NULL');
+        return $qb->getQuery()->getResult();
+    }
+
+    public function getPackageMaintainersQueryBuilder(Package $package, User $excludeUser=null)
+    {
+        $qb = $this->createQueryBuilder('u')
+            ->select('u')
+            ->innerJoin('u.packages', 'p', 'WITH', 'p.id = :packageId')
+            ->setParameter(':packageId', $package->getId())
+            ->orderBy('u.username', 'ASC');
+
+        if ($excludeUser) {
+            $qb->andWhere('u.id <> :userId')
+                ->setParameter(':userId', $excludeUser->getId());
+        }
+
+        return $qb;
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/Version.php b/src/Packagist/WebBundle/Entity/Version.php
new file mode 100644
index 0000000..0a7ed1b
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/Version.php
@@ -0,0 +1,923 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Composer\Package\Version\VersionParser;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
+
+/**
+ * @ORM\Entity(repositoryClass="Packagist\WebBundle\Entity\VersionRepository")
+ * @ORM\Table(
+ *     name="package_version",
+ *     uniqueConstraints={@ORM\UniqueConstraint(name="pkg_ver_idx",columns={"package_id","normalizedVersion"})},
+ *     indexes={
+ *         @ORM\Index(name="release_idx",columns={"releasedAt"}),
+ *         @ORM\Index(name="is_devel_idx",columns={"development"})
+ *     }
+ * )
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Version
+{
+    /**
+     * @ORM\Id
+     * @ORM\Column(type="integer")
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    private $id;
+
+    /**
+     * @ORM\Column
+     * @Assert\NotBlank()
+     */
+    private $name;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $description;
+
+    /**
+     * @ORM\Column(nullable=true)
+     */
+    private $type;
+
+    /**
+     * @ORM\Column(nullable=true)
+     */
+    private $targetDir;
+
+    /**
+     * @ORM\Column(type="array", nullable=true)
+     */
+    private $extra = array();
+
+    /**
+     * @ORM\ManyToMany(targetEntity="Packagist\WebBundle\Entity\Tag", inversedBy="versions")
+     * @ORM\JoinTable(name="version_tag",
+     *     joinColumns={@ORM\JoinColumn(name="version_id", referencedColumnName="id")},
+     *     inverseJoinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
+     * )
+     */
+    private $tags;
+
+    /**
+     * @ORM\ManyToOne(targetEntity="Packagist\WebBundle\Entity\Package", fetch="EAGER", inversedBy="versions")
+     * @Assert\Type(type="Packagist\WebBundle\Entity\Package")
+     */
+    private $package;
+
+    /**
+     * @ORM\Column(nullable=true)
+     * @Assert\Url()
+     */
+    private $homepage;
+
+    /**
+     * @ORM\Column
+     * @Assert\NotBlank()
+     */
+    private $version;
+
+    /**
+     * @ORM\Column(length=191)
+     * @Assert\NotBlank()
+     */
+    private $normalizedVersion;
+
+    /**
+     * @ORM\Column(type="boolean")
+     * @Assert\NotBlank()
+     */
+    private $development;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $license;
+
+    /**
+     * @ORM\ManyToMany(targetEntity="Packagist\WebBundle\Entity\Author", inversedBy="versions")
+     * @ORM\JoinTable(name="version_author",
+     *     joinColumns={@ORM\JoinColumn(name="version_id", referencedColumnName="id")},
+     *     inverseJoinColumns={@ORM\JoinColumn(name="author_id", referencedColumnName="id")}
+     * )
+     */
+    private $authors;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\RequireLink", mappedBy="version")
+     */
+    private $require;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\ReplaceLink", mappedBy="version")
+     */
+    private $replace;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\ConflictLink", mappedBy="version")
+     */
+    private $conflict;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\ProvideLink", mappedBy="version")
+     */
+    private $provide;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\DevRequireLink", mappedBy="version")
+     */
+    private $devRequire;
+
+    /**
+     * @ORM\OneToMany(targetEntity="Packagist\WebBundle\Entity\SuggestLink", mappedBy="version")
+     */
+    private $suggest;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $source;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $dist;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $autoload;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $binaries;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $includePaths;
+
+    /**
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $support;
+
+    /**
+     * @ORM\Column(type="datetime")
+     */
+    private $createdAt;
+
+    /**
+     * @ORM\Column(type="datetime")
+     */
+    private $updatedAt;
+
+    /**
+     * @ORM\Column(type="datetime", nullable=true)
+     */
+    private $releasedAt;
+
+    public function __construct()
+    {
+        $this->tags = new ArrayCollection();
+        $this->require = new ArrayCollection();
+        $this->replace = new ArrayCollection();
+        $this->conflict = new ArrayCollection();
+        $this->provide = new ArrayCollection();
+        $this->devRequire = new ArrayCollection();
+        $this->suggest = new ArrayCollection();
+        $this->authors = new ArrayCollection();
+        $this->createdAt = new \DateTime;
+        $this->updatedAt = new \DateTime;
+    }
+
+    public function toArray()
+    {
+        $tags = array();
+        foreach ($this->getTags() as $tag) {
+            /** @var $tag Tag */
+            $tags[] = $tag->getName();
+        }
+        $authors = array();
+        foreach ($this->getAuthors() as $author) {
+            /** @var $author Author */
+            $authors[] = $author->toArray();
+        }
+
+        $data = array(
+            'name' => $this->getName(),
+            'description' => (string) $this->getDescription(),
+            'keywords' => $tags,
+            'homepage' => (string) $this->getHomepage(),
+            'version' => $this->getVersion(),
+            'version_normalized' => $this->getNormalizedVersion(),
+            'license' => $this->getLicense(),
+            'authors' => $authors,
+            'source' => $this->getSource(),
+            'dist' => $this->getDist(),
+            'type' => $this->getType(),
+        );
+
+        if ($this->getReleasedAt()) {
+            $data['time'] = $this->getReleasedAt()->format('Y-m-d\TH:i:sP');
+        }
+        if ($this->getAutoload()) {
+            $data['autoload'] = $this->getAutoload();
+        }
+        if ($this->getExtra()) {
+            $data['extra'] = $this->getExtra();
+        }
+        if ($this->getTargetDir()) {
+            $data['target-dir'] = $this->getTargetDir();
+        }
+        if ($this->getIncludePaths()) {
+            $data['include-path'] = $this->getIncludePaths();
+        }
+        if ($this->getBinaries()) {
+            $data['bin'] = $this->getBinaries();
+        }
+
+        $supportedLinkTypes = array(
+            'require'    => 'require',
+            'devRequire' => 'require-dev',
+            'suggest'    => 'suggest',
+            'conflict'   => 'conflict',
+            'provide'    => 'provide',
+            'replace'    => 'replace',
+        );
+
+        foreach ($supportedLinkTypes as $method => $linkType) {
+            foreach ($this->{'get'.$method}() as $link) {
+                $link = $link->toArray();
+                $data[$linkType][key($link)] = current($link);
+            }
+        }
+
+        if ($this->getPackage()->isAbandoned()) {
+            $data['abandoned'] = $this->getPackage()->getReplacementPackage() ?: true;
+        }
+
+        return $data;
+    }
+
+    public function equals(Version $version)
+    {
+        return strtolower($version->getName()) === strtolower($this->getName())
+            && strtolower($version->getNormalizedVersion()) === strtolower($this->getNormalizedVersion());
+    }
+
+    /**
+     * Get id
+     *
+     * @return string $id
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set name
+     *
+     * @param string $name
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * Get name
+     *
+     * @return string $name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getNames()
+    {
+        $names = array(
+            strtolower($this->name) => true
+        );
+
+        foreach ($this->getReplace() as $link) {
+            $names[strtolower($link->getPackageName())] = true;
+        }
+
+        foreach ($this->getProvide() as $link) {
+            $names[strtolower($link->getPackageName())] = true;
+        }
+
+        return array_keys($names);
+    }
+
+    /**
+     * Set description
+     *
+     * @param string $description
+     */
+    public function setDescription($description)
+    {
+        $this->description = $description;
+    }
+
+    /**
+     * Get description
+     *
+     * @return string $description
+     */
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    /**
+     * Set homepage
+     *
+     * @param string $homepage
+     */
+    public function setHomepage($homepage)
+    {
+        $this->homepage = $homepage;
+    }
+
+    /**
+     * Get homepage
+     *
+     * @return string $homepage
+     */
+    public function getHomepage()
+    {
+        return $this->homepage;
+    }
+
+    /**
+     * Set version
+     *
+     * @param string $version
+     */
+    public function setVersion($version)
+    {
+        $this->version = $version;
+    }
+
+    /**
+     * Get version
+     *
+     * @return string $version
+     */
+    public function getVersion()
+    {
+        return $this->version;
+    }
+
+    /**
+     * @return string
+     */
+    public function getRequireVersion()
+    {
+        return preg_replace('{^v(\d)}', '$1', str_replace('.x-dev', '.*@dev', $this->getVersion()));
+    }
+
+    /**
+     * Set normalizedVersion
+     *
+     * @param string $normalizedVersion
+     */
+    public function setNormalizedVersion($normalizedVersion)
+    {
+        $this->normalizedVersion = $normalizedVersion;
+    }
+
+    /**
+     * Get normalizedVersion
+     *
+     * @return string $normalizedVersion
+     */
+    public function getNormalizedVersion()
+    {
+        return $this->normalizedVersion;
+    }
+
+    /**
+     * Set license
+     *
+     * @param array $license
+     */
+    public function setLicense(array $license)
+    {
+        $this->license = json_encode($license);
+    }
+
+    /**
+     * Get license
+     *
+     * @return array $license
+     */
+    public function getLicense()
+    {
+        return json_decode($this->license, true);
+    }
+
+    /**
+     * Set source
+     *
+     * @param array $source
+     */
+    public function setSource($source)
+    {
+        $this->source = null === $source ? $source : json_encode($source);
+    }
+
+    /**
+     * Get source
+     *
+     * @return array|null
+     */
+    public function getSource()
+    {
+        return json_decode($this->source, true);
+    }
+
+    /**
+     * Set dist
+     *
+     * @param array $dist
+     */
+    public function setDist($dist)
+    {
+        $this->dist = null === $dist ? $dist : json_encode($dist);
+    }
+
+    /**
+     * Get dist
+     *
+     * @return array|null
+     */
+    public function getDist()
+    {
+        return json_decode($this->dist, true);
+    }
+
+    /**
+     * Set autoload
+     *
+     * @param array $autoload
+     */
+    public function setAutoload($autoload)
+    {
+        $this->autoload = json_encode($autoload);
+    }
+
+    /**
+     * Get autoload
+     *
+     * @return array|null
+     */
+    public function getAutoload()
+    {
+        return json_decode($this->autoload, true);
+    }
+
+    /**
+     * Set binaries
+     *
+     * @param array $binaries
+     */
+    public function setBinaries($binaries)
+    {
+        $this->binaries = null === $binaries ? $binaries : json_encode($binaries);
+    }
+
+    /**
+     * Get binaries
+     *
+     * @return array|null
+     */
+    public function getBinaries()
+    {
+        return json_decode($this->binaries, true);
+    }
+
+    /**
+     * Set include paths.
+     *
+     * @param array $paths
+     */
+    public function setIncludePaths($paths)
+    {
+        $this->includePaths = $paths ? json_encode($paths) : null;
+    }
+
+    /**
+     * Get include paths.
+     *
+     * @return array|null
+     */
+    public function getIncludePaths()
+    {
+        return json_decode($this->includePaths, true);
+    }
+
+    /**
+     * Set support
+     *
+     * @param array $support
+     */
+    public function setSupport($support)
+    {
+        $this->support = $support ? json_encode($support) : null;
+    }
+
+    /**
+     * Get support
+     *
+     * @return array|null
+     */
+    public function getSupport()
+    {
+        return json_decode($this->support, true);
+    }
+
+    /**
+     * Set createdAt
+     *
+     * @param \DateTime $createdAt
+     */
+    public function setCreatedAt($createdAt)
+    {
+        $this->createdAt = $createdAt;
+    }
+
+    /**
+     * Get createdAt
+     *
+     * @return \DateTime
+     */
+    public function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
+
+    /**
+     * Set releasedAt
+     *
+     * @param \DateTime $releasedAt
+     */
+    public function setReleasedAt($releasedAt)
+    {
+        $this->releasedAt = $releasedAt;
+    }
+
+    /**
+     * Get releasedAt
+     *
+     * @return \DateTime
+     */
+    public function getReleasedAt()
+    {
+        return $this->releasedAt;
+    }
+
+    /**
+     * Set package
+     *
+     * @param Package $package
+     */
+    public function setPackage(Package $package)
+    {
+        $this->package = $package;
+    }
+
+    /**
+     * Get package
+     *
+     * @return Package
+     */
+    public function getPackage()
+    {
+        return $this->package;
+    }
+
+    /**
+     * Get tags
+     *
+     * @return Tag[]
+     */
+    public function getTags()
+    {
+        return $this->tags;
+    }
+
+    /**
+     * Set updatedAt
+     *
+     * @param \DateTime $updatedAt
+     */
+    public function setUpdatedAt($updatedAt)
+    {
+        $this->updatedAt = $updatedAt;
+    }
+
+    /**
+     * Get updatedAt
+     *
+     * @return \DateTime $updatedAt
+     */
+    public function getUpdatedAt()
+    {
+        return $this->updatedAt;
+    }
+
+    /**
+     * Get authors
+     *
+     * @return Author[]
+     */
+    public function getAuthors()
+    {
+        return $this->authors;
+    }
+
+    /**
+     * Set type
+     *
+     * @param string $type
+     */
+    public function setType($type)
+    {
+        $this->type = $type;
+    }
+
+    /**
+     * Get type
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Set targetDir
+     *
+     * @param string $targetDir
+     */
+    public function setTargetDir($targetDir)
+    {
+        $this->targetDir = $targetDir;
+    }
+
+    /**
+     * Get targetDir
+     *
+     * @return string
+     */
+    public function getTargetDir()
+    {
+        return $this->targetDir;
+    }
+
+    /**
+     * Set extra
+     *
+     * @param array $extra
+     */
+    public function setExtra($extra)
+    {
+        $this->extra = $extra;
+    }
+
+    /**
+     * Get extra
+     *
+     * @return array
+     */
+    public function getExtra()
+    {
+        return $this->extra;
+    }
+
+    /**
+     * Set development
+     *
+     * @param Boolean $development
+     */
+    public function setDevelopment($development)
+    {
+        $this->development = $development;
+    }
+
+    /**
+     * Get development
+     *
+     * @return Boolean
+     */
+    public function getDevelopment()
+    {
+        return $this->development;
+    }
+
+    /**
+     * @return Boolean
+     */
+    public function isDevelopment()
+    {
+        return $this->getDevelopment();
+    }
+
+    /**
+     * Add tag
+     *
+     * @param Tag $tag
+     */
+    public function addTag(Tag $tag)
+    {
+        $this->tags[] = $tag;
+    }
+
+    /**
+     * Add authors
+     *
+     * @param Author $author
+     */
+    public function addAuthor(Author $author)
+    {
+        $this->authors[] = $author;
+    }
+
+    /**
+     * Add require
+     *
+     * @param RequireLink $require
+     */
+    public function addRequireLink(RequireLink $require)
+    {
+        $this->require[] = $require;
+    }
+
+    /**
+     * Get require
+     *
+     * @return RequireLink[]
+     */
+    public function getRequire()
+    {
+        return $this->require;
+    }
+
+    /**
+     * Add replace
+     *
+     * @param ReplaceLink $replace
+     */
+    public function addReplaceLink(ReplaceLink $replace)
+    {
+        $this->replace[] = $replace;
+    }
+
+    /**
+     * Get replace
+     *
+     * @return ReplaceLink[]
+     */
+    public function getReplace()
+    {
+        return $this->replace;
+    }
+
+    /**
+     * Add conflict
+     *
+     * @param ConflictLink $conflict
+     */
+    public function addConflictLink(ConflictLink $conflict)
+    {
+        $this->conflict[] = $conflict;
+    }
+
+    /**
+     * Get conflict
+     *
+     * @return ConflictLink[]
+     */
+    public function getConflict()
+    {
+        return $this->conflict;
+    }
+
+    /**
+     * Add provide
+     *
+     * @param ProvideLink $provide
+     */
+    public function addProvideLink(ProvideLink $provide)
+    {
+        $this->provide[] = $provide;
+    }
+
+    /**
+     * Get provide
+     *
+     * @return ProvideLink[]
+     */
+    public function getProvide()
+    {
+        return $this->provide;
+    }
+
+    /**
+     * Add devRequire
+     *
+     * @param DevRequireLink $devRequire
+     */
+    public function addDevRequireLink(DevRequireLink $devRequire)
+    {
+        $this->devRequire[] = $devRequire;
+    }
+
+    /**
+     * Get devRequire
+     *
+     * @return DevRequireLink[]
+     */
+    public function getDevRequire()
+    {
+        return $this->devRequire;
+    }
+
+    /**
+     * Add suggest
+     *
+     * @param SuggestLink $suggest
+     */
+    public function addSuggestLink(SuggestLink $suggest)
+    {
+        $this->suggest[] = $suggest;
+    }
+
+    /**
+     * Get suggest
+     *
+     * @return SuggestLink[]
+     */
+    public function getSuggest()
+    {
+        return $this->suggest;
+    }
+
+    /**
+     * @return Boolean
+     */
+    public function hasVersionAlias()
+    {
+        return $this->getDevelopment() && $this->getVersionAlias();
+    }
+
+    /**
+     * @return string
+     */
+    public function getVersionAlias()
+    {
+        $extra = $this->getExtra();
+
+        if (isset($extra['branch-alias'][$this->getVersion()])) {
+            $parser = new VersionParser;
+            $version = $parser->normalizeBranch(str_replace('-dev', '', $extra['branch-alias'][$this->getVersion()]));
+            return preg_replace('{(\.9{7})+}', '.x', $version);
+        }
+
+        return '';
+    }
+
+    /**
+     * @return string
+     */
+    public function getRequireVersionAlias()
+    {
+        return str_replace('.x-dev', '.*@dev', $this->getVersionAlias());
+    }
+
+    public function __toString()
+    {
+        return $this->name.' '.$this->version.' ('.$this->normalizedVersion.')';
+    }
+}
diff --git a/src/Packagist/WebBundle/Entity/VersionRepository.php b/src/Packagist/WebBundle/Entity/VersionRepository.php
new file mode 100644
index 0000000..3c2ab3d
--- /dev/null
+++ b/src/Packagist/WebBundle/Entity/VersionRepository.php
@@ -0,0 +1,107 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Entity;
+
+use Doctrine\ORM\EntityRepository;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class VersionRepository extends EntityRepository
+{
+    protected $supportedLinkTypes = array(
+        'require',
+        'conflict',
+        'provide',
+        'replace',
+        'devRequire',
+        'suggest',
+    );
+
+    public function remove(Version $version)
+    {
+        $em = $this->getEntityManager();
+        $version->getPackage()->getVersions()->removeElement($version);
+        $version->getPackage()->setCrawledAt(new \DateTime);
+        $version->getPackage()->setUpdatedAt(new \DateTime);
+
+        $em->getConnection()->executeQuery('DELETE FROM version_author WHERE version_id=:id', array('id' => $version->getId()));
+        $em->getConnection()->executeQuery('DELETE FROM version_tag WHERE version_id=:id', array('id' => $version->getId()));
+        $em->getConnection()->executeQuery('DELETE FROM link_suggest WHERE version_id=:id', array('id' => $version->getId()));
+        $em->getConnection()->executeQuery('DELETE FROM link_conflict WHERE version_id=:id', array('id' => $version->getId()));
+        $em->getConnection()->executeQuery('DELETE FROM link_replace WHERE version_id=:id', array('id' => $version->getId()));
+        $em->getConnection()->executeQuery('DELETE FROM link_provide WHERE version_id=:id', array('id' => $version->getId()));
+        $em->getConnection()->executeQuery('DELETE FROM link_require_dev WHERE version_id=:id', array('id' => $version->getId()));
+        $em->getConnection()->executeQuery('DELETE FROM link_require WHERE version_id=:id', array('id' => $version->getId()));
+
+        $em->remove($version);
+    }
+
+    public function getFullVersion($versionId)
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('v', 't', 'a')
+            ->from('Packagist\WebBundle\Entity\Version', 'v')
+            ->leftJoin('v.tags', 't')
+            ->leftJoin('v.authors', 'a')
+            ->where('v.id = :id')
+            ->setParameter('id', $versionId);
+
+        return $qb->getQuery()->getSingleResult();
+    }
+
+    /**
+     * Returns the latest versions released
+     *
+     * @param string $vendor optional vendor filter
+     * @param string $package optional vendor/package filter
+     * @return \Doctrine\ORM\QueryBuilder
+     */
+    public function getQueryBuilderForLatestVersionWithPackage($vendor = null, $package = null)
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('v')
+            ->from('Packagist\WebBundle\Entity\Version', 'v')
+            ->where('v.development = false')
+            ->orderBy('v.releasedAt', 'DESC');
+
+        if ($vendor || $package) {
+            $qb->innerJoin('v.package', 'p')
+                ->addSelect('p');
+        }
+
+        if ($vendor) {
+            $qb->andWhere('p.name LIKE ?0');
+            $qb->setParameter(0, $vendor.'/%');
+        } elseif ($package) {
+            $qb->andWhere('p.name = ?0')
+                ->setParameter(0, $package);
+        }
+
+        return $qb;
+    }
+
+    public function getLatestReleases($count = 10)
+    {
+        $qb = $this->getEntityManager()->createQueryBuilder();
+        $qb->select('v')
+            ->from('Packagist\WebBundle\Entity\Version', 'v')
+            ->where('v.development = false')
+            ->andWhere('v.releasedAt < :now')
+            ->orderBy('v.releasedAt', 'DESC')
+            ->setMaxResults($count)
+            ->setParameter('now', date('Y-m-d H:i:s'));
+
+        return $qb->getQuery()->useResultCache(true, 900, 'new_releases')->getResult();
+    }
+}
diff --git a/src/Packagist/WebBundle/EventListener/CacheListener.php b/src/Packagist/WebBundle/EventListener/CacheListener.php
new file mode 100644
index 0000000..3f79999
--- /dev/null
+++ b/src/Packagist/WebBundle/EventListener/CacheListener.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Packagist\WebBundle\EventListener;
+
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+
+class CacheListener
+{
+    public function onResponse(FilterResponseEvent $e)
+    {
+        $resp = $e->getResponse();
+
+        // add nginx-cache compatible header
+        if ($resp->headers->hasCacheControlDirective('public') && ($cache = $resp->headers->getCacheControlDirective('s-maxage'))) {
+            $resp->headers->set('X-Accel-Expires', $cache);
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/EventListener/RegistrationListener.php b/src/Packagist/WebBundle/EventListener/RegistrationListener.php
new file mode 100644
index 0000000..c80f993
--- /dev/null
+++ b/src/Packagist/WebBundle/EventListener/RegistrationListener.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Packagist\WebBundle\EventListener;
+
+use FOS\UserBundle\Event\FormEvent;
+use FOS\UserBundle\FOSUserEvents;
+use FOS\UserBundle\Util\TokenGenerator;
+use Packagist\WebBundle\Entity\User;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @author Sullivan Senechal <soullivaneuh@gmail.com>
+ */
+class RegistrationListener implements EventSubscriberInterface
+{
+    /**
+     * @var TokenGenerator
+     */
+    private $tokenGenerator;
+
+    /**
+     * @param TokenGenerator $tokenGenerator
+     */
+    public function __construct(TokenGenerator $tokenGenerator)
+    {
+        $this->tokenGenerator = $tokenGenerator;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public static function getSubscribedEvents()
+    {
+        return [
+            FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess'
+        ];
+    }
+
+    public function onRegistrationSuccess(FormEvent $event)
+    {
+        /** @var User $user */
+        $user = $event->getForm()->getData();
+        $apiToken = substr($this->tokenGenerator->generateToken(), 0, 20);
+        $user->setApiToken($apiToken);
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Handler/OAuthRegistrationFormHandler.php b/src/Packagist/WebBundle/Form/Handler/OAuthRegistrationFormHandler.php
new file mode 100644
index 0000000..9f42b80
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Handler/OAuthRegistrationFormHandler.php
@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Handler;
+
+use FOS\UserBundle\Model\UserManagerInterface;
+use FOS\UserBundle\Util\TokenGeneratorInterface;
+use HWI\Bundle\OAuthBundle\Form\RegistrationFormHandlerInterface;
+use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
+use Symfony\Component\Form\Form;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * OAuthRegistrationFormHandler
+ *
+ * @author Alexander <iam.asm89@gmail.com>
+ */
+class OAuthRegistrationFormHandler implements RegistrationFormHandlerInterface
+{
+    private $userManager;
+    private $tokenGenerator;
+
+    /**
+     * Constructor.
+     *
+     * @param UserManagerInterface $userManager
+     * @param TokenGeneratorInterface $tokenGenerator
+     */
+    public function __construct(UserManagerInterface $userManager, TokenGeneratorInterface $tokenGenerator)
+    {
+        $this->tokenGenerator = $tokenGenerator;
+        $this->userManager = $userManager;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function process(Request $request, Form $form, UserResponseInterface $userInformation)
+    {
+        $user = $this->userManager->createUser();
+
+        // Try to get some properties for the initial form when coming from github
+        if ('GET' === $request->getMethod()) {
+            $user->setUsername($this->getUniqueUsername($userInformation->getNickname()));
+            $user->setEmail($userInformation->getEmail());
+        }
+
+        $form->setData($user);
+
+        if ('POST' === $request->getMethod()) {
+            $form->handleRequest($request);
+
+            if ($form->isValid()) {
+                if (!$user->getPassword() && !$user->getPlainPassword()) {
+                    $randomPassword = $this->tokenGenerator->generateToken();
+                    $user->setPlainPassword($randomPassword);
+                }
+                $user->setEnabled(true);
+
+                $apiToken = substr($this->tokenGenerator->generateToken(), 0, 20);
+                $user->setApiToken($apiToken);
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempts to get a unique username for the user.
+     *
+     * @param string $name
+     *
+     * @return string Name, or empty string if it failed after 10 times
+     *
+     * @see HWI\Bundle\OAuthBundle\Form\FOSUBRegistrationHandler
+     */
+    protected function getUniqueUserName($name)
+    {
+        $i = 0;
+        $testName = $name;
+
+        do {
+            $user = $this->userManager->findUserByUsername($testName);
+        } while ($user !== null && $i < 10 && $testName = $name.++$i);
+
+        return $user !== null ? '' : $testName;
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Model/MaintainerRequest.php b/src/Packagist/WebBundle/Form/Model/MaintainerRequest.php
new file mode 100644
index 0000000..a90f212
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Model/MaintainerRequest.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Model;
+
+use FOS\UserBundle\Model\UserInterface;
+
+class MaintainerRequest
+{
+    protected $user;
+
+    public function setUser(UserInterface $user)
+    {
+        $this->user = $user;
+    }
+
+    public function getUser()
+    {
+        return $this->user;
+    }
+}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Form/Model/OrderBy.php b/src/Packagist/WebBundle/Form/Model/OrderBy.php
new file mode 100644
index 0000000..165b61a
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Model/OrderBy.php
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Model;
+
+/**
+ * @author Benjamin Michalski <benjamin.michalski@gmail.com>
+ */
+class OrderBy
+{
+    /**
+     * @var string
+     */
+    protected $sort;
+
+    /**
+     * @var string
+     */
+    protected $order;
+
+    public function setSort($sort)
+    {
+        $this->sort = $sort;
+    }
+
+    public function getSort()
+    {
+        return $this->sort;
+    }
+
+    public function setOrder($order)
+    {
+        $this->order = $order;
+    }
+
+    public function getOrder()
+    {
+        return $this->order;
+    }
+}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Form/Model/SearchQuery.php b/src/Packagist/WebBundle/Form/Model/SearchQuery.php
new file mode 100644
index 0000000..cc0d243
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Model/SearchQuery.php
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Model;
+
+use Symfony\Component\Validator\Constraints as Assert;
+
+class SearchQuery
+{
+    /**
+     * @Assert\NotBlank()
+     */
+    protected $query;
+
+    /**
+     * @var array
+     */
+    protected $orderBys;
+
+    public function setQuery($query)
+    {
+        $this->query = $query;
+    }
+
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    public function setOrderBys($orderBys)
+    {
+        $this->orderBys = $orderBys;
+    }
+
+    public function getOrderBys()
+    {
+        return $this->orderBys;
+    }
+}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Form/Type/AbandonedType.php b/src/Packagist/WebBundle/Form/Type/AbandonedType.php
new file mode 100644
index 0000000..20cc513
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/AbandonedType.php
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\FormBuilderInterface;
+
+/**
+ * Class AbandonedType
+ *
+ * Form used to acquire replacement Package information for abandoned package.
+ *
+ * @package Packagist\WebBundle\Form\Type
+ */
+class AbandonedType extends AbstractType
+{
+    /**
+     * @param FormBuilderInterface $builder
+     * @param array $options
+     */
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder->add(
+            'replacement',
+            TextType::class,
+            array(
+                'required' => false,
+                'label'    => 'Replacement package',
+                'attr'     => array('placeholder' => 'optional')
+            )
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'package';
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Type/AddMaintainerRequestType.php b/src/Packagist/WebBundle/Form/Type/AddMaintainerRequestType.php
new file mode 100644
index 0000000..fc2ba35
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/AddMaintainerRequestType.php
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use FOS\UserBundle\Form\Type\UsernameFormType;
+use Packagist\WebBundle\Form\Model\MaintainerRequest;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class AddMaintainerRequestType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder->add('user', UsernameFormType::class);
+    }
+
+    public function configureOptions(OptionsResolver $resolver)
+    {
+        $resolver->setDefaults(array(
+            'data_class' => MaintainerRequest::class,
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'add_maintainer_form';
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Type/OAuthRegistrationFormType.php b/src/Packagist/WebBundle/Form/Type/OAuthRegistrationFormType.php
new file mode 100644
index 0000000..f5e243f
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/OAuthRegistrationFormType.php
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use Packagist\WebBundle\Entity\User;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\EmailType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class OAuthRegistrationFormType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder
+            ->add('username', null, array('label' => 'form.username', 'translation_domain' => 'FOSUserBundle'))
+            ->add('email', EmailType::class, array('label' => 'form.email', 'translation_domain' => 'FOSUserBundle'))
+        ;
+    }
+
+    public function configureOptions(OptionsResolver $resolver)
+    {
+        $resolver->setDefaults(array(
+            'data_class' => User::class,
+            'intention'  => 'registration',
+            'validation_groups' => array('Default', 'Profile'),
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'packagist_oauth_user_registration';
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Type/OrderByType.php b/src/Packagist/WebBundle/Form/Type/OrderByType.php
new file mode 100644
index 0000000..89bcce0
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/OrderByType.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use Packagist\WebBundle\Form\Model\OrderBy;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * @author Benjamin Michalski <benjamin.michalski@gmail.com>
+ */
+class OrderByType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder->add('sort');
+        $builder->add('order', ChoiceType::class, array(
+            'choices' => array(
+                'asc' => 'asc',
+                'desc' => 'desc'
+            ),
+            'choices_as_values' => true,
+        ));
+    }
+
+    public function configureOptions(OptionsResolver $resolver)
+    {
+        $resolver->setDefaults(array(
+            'data_class' => OrderBy::class,
+            'csrf_protection' => false,
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'order_by';
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Type/PackageType.php b/src/Packagist/WebBundle/Form/Type/PackageType.php
new file mode 100644
index 0000000..1aae455
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/PackageType.php
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use Packagist\WebBundle\Entity\Package;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class PackageType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder->add('repository', TextType::class, array(
+            'label' => 'Repository URL (Git/Svn/Hg)',
+            'attr'  => array(
+                'placeholder' => 'i.e.: git://github.com/composer/composer.git',
+            )
+        ));
+    }
+
+    public function configureOptions(OptionsResolver $resolver)
+    {
+        $resolver->setDefaults(array(
+            'data_class' => Package::class,
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'package';
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Type/ProfileFormType.php b/src/Packagist/WebBundle/Form/Type/ProfileFormType.php
new file mode 100644
index 0000000..8be285b
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/ProfileFormType.php
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use FOS\UserBundle\Form\Type\ProfileFormType as BaseType;
+use Symfony\Component\Form\FormBuilderInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ProfileFormType extends BaseType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        parent::buildForm($builder, $options);
+
+        $builder->add('failureNotifications', null, array(
+            'required' => false,
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'packagist_user_profile';
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Type/RemoveMaintainerRequestType.php b/src/Packagist/WebBundle/Form/Type/RemoveMaintainerRequestType.php
new file mode 100644
index 0000000..47eaa39
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/RemoveMaintainerRequestType.php
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use Doctrine\ORM\EntityRepository;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\User;
+use Packagist\WebBundle\Form\Model\MaintainerRequest;
+use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class RemoveMaintainerRequestType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder->add('user', EntityType::class, array(
+            'class' => 'PackagistWebBundle:User',
+            'query_builder' => function(EntityRepository $er) use ($options) {
+                return $er->getPackageMaintainersQueryBuilder($options['package'], $options['excludeUser']);
+            },
+        ));
+    }
+
+    public function configureOptions(OptionsResolver $resolver)
+    {
+        $resolver->setRequired(array('package'));
+        $resolver->setDefaults(array(
+            'excludeUser' => null,
+            'data_class' => MaintainerRequest::class
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'remove_maintainer_form';
+    }
+}
diff --git a/src/Packagist/WebBundle/Form/Type/SearchQueryType.php b/src/Packagist/WebBundle/Form/Type/SearchQueryType.php
new file mode 100644
index 0000000..5515856
--- /dev/null
+++ b/src/Packagist/WebBundle/Form/Type/SearchQueryType.php
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Form\Type;
+
+use Packagist\WebBundle\Form\Model\SearchQuery;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\CollectionType;
+use Symfony\Component\Form\Extension\Core\Type\SearchType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+class SearchQueryType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder->add('query', SearchType::class);
+        $builder->add('orderBys', CollectionType::class, array(
+            'entry_type' => OrderByType::class,
+            'allow_add' => true,
+            'allow_delete' => true,
+            'prototype' => false,
+        ));
+    }
+
+    public function configureOptions(OptionsResolver $resolver)
+    {
+        $resolver->setDefaults(array(
+            'data_class' => SearchQuery::class,
+            'csrf_protection' => false,
+            'method' => 'GET',
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBlockPrefix()
+    {
+        return 'search_query';
+    }
+}
diff --git a/src/Packagist/WebBundle/Menu/MenuBuilder.php b/src/Packagist/WebBundle/Menu/MenuBuilder.php
new file mode 100644
index 0000000..36e5cfe
--- /dev/null
+++ b/src/Packagist/WebBundle/Menu/MenuBuilder.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Packagist\WebBundle\Menu;
+
+use Knp\Menu\FactoryInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+
+class MenuBuilder
+{
+    private $factory;
+    private $username;
+
+    /**
+     * @param FactoryInterface      $factory
+     * @param TokenStorageInterface $tokenStorage
+     */
+    public function __construct(FactoryInterface $factory, TokenStorageInterface $tokenStorage)
+    {
+        $this->factory = $factory;
+
+        if ($tokenStorage->getToken() && $tokenStorage->getToken()->getUser()) {
+            $this->username = $tokenStorage->getToken()->getUser()->getUsername();
+        }
+    }
+
+    public function createUserMenu()
+    {
+        $menu = $this->factory->createItem('root');
+        $menu->setChildrenAttribute('class', 'list-unstyled');
+
+        $this->addProfileMenu($menu);
+        $menu->addChild('hr', array('label' => '<hr>', 'labelAttributes' => array('class' => 'normal'), 'extras' => array('safe_label' => true)));
+        $menu->addChild('Logout', array('label' => '<span class="icon-off"></span>Logout', 'route' => 'logout', 'extras' => array('safe_label' => true)));
+
+        return $menu;
+    }
+
+    public function createProfileMenu()
+    {
+        $menu = $this->factory->createItem('root');
+        $menu->setChildrenAttribute('class', 'nav nav-tabs nav-stacked');
+
+        $this->addProfileMenu($menu);
+
+        return $menu;
+    }
+
+    private function addProfileMenu($menu)
+    {
+        $menu->addChild('Profile', array('label' => '<span class="icon-vcard"></span>Profile', 'route' => 'fos_user_profile_show', 'extras' => array('safe_label' => true)));
+        $menu->addChild('Settings', array('label' => '<span class="icon-tools"></span>Settings', 'route' => 'fos_user_profile_edit', 'extras' => array('safe_label' => true)));
+        $menu->addChild('Change password', array('label' => '<span class="icon-key"></span>Change password', 'route' => 'fos_user_change_password', 'extras' => array('safe_label' => true)));
+        $menu->addChild('My packages', array('label' => '<span class="icon-box"></span>My packages', 'route' => 'user_packages', 'routeParameters' => array('name' => $this->username), 'extras' => array('safe_label' => true)));
+        $menu->addChild('My favorites', array('label' => '<span class="icon-leaf"></span>My favorites', 'route' => 'user_favorites', 'routeParameters' => array('name' => $this->username), 'extras' => array('safe_label' => true)));
+    }
+}
diff --git a/src/Packagist/WebBundle/Model/DownloadManager.php b/src/Packagist/WebBundle/Model/DownloadManager.php
new file mode 100644
index 0000000..94da65c
--- /dev/null
+++ b/src/Packagist/WebBundle/Model/DownloadManager.php
@@ -0,0 +1,142 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Model;
+
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\Version;
+use Predis\Client;
+
+/**
+ * Manages the download counts for packages.
+ */
+class DownloadManager
+{
+    protected $redis;
+    protected $redisCommandLoaded = false;
+
+    public function __construct(Client $redis)
+    {
+        $this->redis = $redis;
+    }
+
+    /**
+     * Gets the total, monthly, and daily download counts for an entire package or optionally a version.
+     *
+     * @param \Packagist\WebBundle\Entity\Package|int      $package
+     * @param \Packagist\WebBundle\Entity\Version|int|null $version
+     * @return array
+     */
+    public function getDownloads($package, $version = null)
+    {
+        if ($package instanceof Package) {
+            $package = $package->getId();
+        }
+
+        if ($version instanceof Version) {
+            $version = $version->getId();
+        }
+
+        if ($version !== null) {
+            $version = '-'.$version;
+        }
+
+        $date = new \DateTime();
+        $keys = array('dl:'.$package . $version);
+        for ($i = 0; $i < 30; $i++) {
+            $keys[] = 'dl:' . $package . $version . ':' . $date->format('Ymd');
+            $date->modify('-1 day');
+        }
+
+        $vals = $this->redis->mget($keys);
+        $result = array(
+            'total' => (int) array_shift($vals) ?: 0,
+            'monthly' => (int) array_sum($vals) ?: 0,
+            'daily' => (int) $vals[0] ?: 0,
+        );
+
+        return $result;
+    }
+
+    /**
+     * Gets the total download count for a package.
+     *
+     * @param \Packagist\WebBundle\Entity\Package|int $package
+     * @return int
+     */
+    public function getTotalDownloads($package)
+    {
+        if ($package instanceof Package) {
+            $package = $package->getId();
+        }
+
+        return (int) $this->redis->get('dl:' . $package) ?: 0;
+    }
+
+    /**
+     * Gets total download counts for multiple package IDs.
+     *
+     * @param array $packageIds
+     * @return array a map of package ID to download count
+     */
+    public function getPackagesDownloads(array $packageIds)
+    {
+        $keys = array();
+
+        foreach ($packageIds as $id) {
+            if (ctype_digit((string) $id)) {
+                $keys[$id] = 'dl:'.$id;
+            }
+        }
+
+        if (!$keys) {
+            return array();
+        }
+
+        $res = array_map('intval', $this->redis->mget(array_values($keys)));
+        return array_combine(array_keys($keys), $res);
+    }
+
+    /**
+     * Tracks a new download by updating the relevant keys.
+     *
+     * @param \Packagist\WebBundle\Entity\Package|int $package
+     * @param \Packagist\WebBundle\Entity\Version|int $version
+     */
+    public function addDownload($package, $version)
+    {
+        $redis = $this->redis;
+
+        if (!$this->redisCommandLoaded) {
+            $redis->getProfile()->defineCommand('downloadsIncr', 'Packagist\Redis\DownloadsIncr');
+            $this->redisCommandLoaded = true;
+        }
+
+        if ($package instanceof Package) {
+            $package = $package->getId();
+        }
+
+        if ($version instanceof Version) {
+            $version = $version->getId();
+        }
+
+        $redis->downloadsIncr(
+            'downloads',
+            'dl:'.$package,
+            'dl:'.$package.':'.date('Ym'),
+            'dl:'.$package.':'.date('Ymd'),
+            'dl:'.$package.'-'.$version,
+            'dl:'.$package.'-'.$version.':'.date('Ym'),
+            'dl:'.$package.'-'.$version.':'.date('Ymd')
+        );
+    }
+}
diff --git a/src/Packagist/WebBundle/Model/FavoriteManager.php b/src/Packagist/WebBundle/Model/FavoriteManager.php
new file mode 100644
index 0000000..2ceaa8c
--- /dev/null
+++ b/src/Packagist/WebBundle/Model/FavoriteManager.php
@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Model;
+
+use FOS\UserBundle\Model\UserInterface;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\PackageRepository;
+use Packagist\WebBundle\Entity\UserRepository;
+use Predis\Client;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class FavoriteManager
+{
+    protected $redis;
+    protected $packageRepo;
+    protected $userRepo;
+
+    public function __construct(Client $redis, PackageRepository $packageRepo, UserRepository $userRepo)
+    {
+        $this->redis = $redis;
+        $this->packageRepo = $packageRepo;
+        $this->userRepo = $userRepo;
+    }
+
+    public function markFavorite(UserInterface $user, Package $package)
+    {
+        if (!$this->isMarked($user, $package)) {
+            $this->redis->zadd('pkg:'.$package->getId().':fav', time(), $user->getId());
+            $this->redis->zadd('usr:'.$user->getId().':fav', time(), $package->getId());
+        }
+    }
+
+    public function removeFavorite(UserInterface $user, Package $package)
+    {
+        $this->redis->zrem('pkg:'.$package->getId().':fav', $user->getId());
+        $this->redis->zrem('usr:'.$user->getId().':fav', $package->getId());
+    }
+
+    public function getFavorites(UserInterface $user, $limit = 0, $offset = 0)
+    {
+        $favoriteIds = $this->redis->zrevrange('usr:'.$user->getId().':fav', $offset, $offset + $limit - 1);
+
+        return $this->packageRepo->findById($favoriteIds);
+    }
+
+    public function getFavoriteCount(UserInterface $user)
+    {
+        return $this->redis->zcard('usr:'.$user->getId().':fav');
+    }
+
+    public function getFavers(Package $package, $offset = 0, $limit = 100)
+    {
+        $faverIds = $this->redis->zrevrange('pkg:'.$package->getId().':fav', $offset, $offset + $limit - 1);
+
+        return $this->userRepo->findById($faverIds);
+    }
+
+    public function getFaverCount(Package $package)
+    {
+        return $this->redis->zcard('pkg:'.$package->getId().':fav') + $package->getGitHubStars();
+    }
+
+    public function getFaverCounts(array $packageIds)
+    {
+        $res = array();
+
+        // TODO should be done with scripting when available
+        foreach ($packageIds as $id) {
+            if (ctype_digit((string) $id)) {
+                $res[$id] = $this->redis->zcard('pkg:'.$id.':fav');
+            }
+        }
+
+        $rows = $this->packageRepo->getGitHubStars($packageIds);
+        foreach ($rows as $row) {
+            $res[$row['id']] += $row['gitHubStars'];
+        }
+
+        return $res;
+    }
+
+    public function isMarked(UserInterface $user, Package $package)
+    {
+        return null !== $this->redis->zrank('usr:'.$user->getId().':fav', $package->getId());
+    }
+}
diff --git a/src/Packagist/WebBundle/Model/PackageManager.php b/src/Packagist/WebBundle/Model/PackageManager.php
new file mode 100644
index 0000000..d7c0547
--- /dev/null
+++ b/src/Packagist/WebBundle/Model/PackageManager.php
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Model;
+
+use Doctrine\ORM\EntityManager;
+use Packagist\WebBundle\Entity\Package;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class PackageManager
+{
+    protected $em;
+    protected $mailer;
+    protected $twig;
+    protected $logger;
+    protected $options;
+
+    public function __construct(EntityManager $em, \Swift_Mailer $mailer, \Twig_Environment $twig, LoggerInterface $logger, array $options)
+    {
+        $this->em = $em;
+        $this->mailer = $mailer;
+        $this->twig = $twig;
+        $this->logger = $logger;
+        $this->options = $options;
+    }
+
+    public function notifyUpdateFailure(Package $package, \Exception $e, $details = null)
+    {
+        if (!$package->isUpdateFailureNotified()) {
+            $recipients = array();
+            foreach ($package->getMaintainers() as $maintainer) {
+                if ($maintainer->isNotifiableForFailures()) {
+                    $recipients[$maintainer->getEmail()] = $maintainer->getUsername();
+                }
+            }
+
+            if ($recipients) {
+                $body = $this->twig->render('PackagistWebBundle:Email:update_failed.txt.twig', array(
+                    'package' => $package,
+                    'exception' => get_class($e),
+                    'exceptionMessage' => $e->getMessage(),
+                    'details' => $details,
+                ));
+
+                $message = \Swift_Message::newInstance()
+                    ->setSubject($package->getName().' failed to update, invalid composer.json data')
+                    ->setFrom($this->options['from'], $this->options['fromName'])
+                    ->setTo($recipients)
+                    ->setBody($body)
+                ;
+
+                try {
+                    $this->mailer->send($message);
+                } catch (\Swift_TransportException $e) {
+                    $this->logger->error('['.get_class($e).'] '.$e->getMessage());
+
+                    return false;
+                }
+            }
+
+            $package->setUpdateFailureNotified(true);
+            $this->em->flush();
+        }
+
+        return true;
+    }
+
+    public function notifyNewMaintainer($user, $package)
+    {
+        $body = $this->twig->render('PackagistWebBundle:Email:maintainer_added.txt.twig', array(
+            'package_name' => $package->getName()
+        ));
+
+        $message = \Swift_Message::newInstance()
+            ->setSubject('You have been added to ' . $package->getName() . ' as a maintainer')
+            ->setFrom($this->options['from'], $this->options['fromName'])
+            ->setTo($user->getEmail())
+            ->setBody($body)
+        ;
+
+        try {
+            $this->mailer->send($message);
+        } catch (\Swift_TransportException $e) {
+            $this->logger->error('['.get_class($e).'] '.$e->getMessage());
+
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/src/Packagist/WebBundle/Model/RedisAdapter.php b/src/Packagist/WebBundle/Model/RedisAdapter.php
new file mode 100644
index 0000000..ce2ede9
--- /dev/null
+++ b/src/Packagist/WebBundle/Model/RedisAdapter.php
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Model;
+
+use Pagerfanta\Adapter\AdapterInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class RedisAdapter implements AdapterInterface
+{
+    protected $model;
+    protected $instance;
+    protected $fetchMethod;
+    protected $countMethod;
+
+    public function __construct($model, $instance, $fetchMethod, $countMethod)
+    {
+        $this->model = $model;
+        $this->instance = $instance;
+        $this->fetchMethod = $fetchMethod;
+        $this->countMethod = $countMethod;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getNbResults()
+    {
+        return $this->model->{$this->countMethod}($this->instance);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getSlice($offset, $length)
+    {
+        return $this->model->{$this->fetchMethod}($this->instance, $length, $offset);
+    }
+}
diff --git a/src/Packagist/WebBundle/Package/BackgroundUpdaterConsumer.php b/src/Packagist/WebBundle/Package/BackgroundUpdaterConsumer.php
new file mode 100644
index 0000000..9aa4f8f
--- /dev/null
+++ b/src/Packagist/WebBundle/Package/BackgroundUpdaterConsumer.php
@@ -0,0 +1,38 @@
+<?php
+namespace Packagist\WebBundle\Package;
+
+use Packagist\WebBundle\Command\UpdatePackagesCommand;
+use PhpAmqpLib\Message\AMQPMessage;
+use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Output\NullOutput;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class BackgroundUpdaterConsumer implements ConsumerInterface
+{
+
+    /**
+     * @var ContainerInterface
+     */
+    protected $container;
+
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    public function execute(AMQPMessage $message)
+    {
+        $data = unserialize($message->body);
+        $package = $data['package_name'];
+
+        $command = new UpdatePackagesCommand();
+        $command->setContainer($this->container);
+        $input = new ArrayInput(array(
+          'package' => $package,
+        ));
+
+        $output = new NullOutput();
+        $resultCode = $command->run($input, $output);
+    }
+}
diff --git a/src/Packagist/WebBundle/Package/BackgroundUpsertConsumer.php b/src/Packagist/WebBundle/Package/BackgroundUpsertConsumer.php
new file mode 100644
index 0000000..00aba39
--- /dev/null
+++ b/src/Packagist/WebBundle/Package/BackgroundUpsertConsumer.php
@@ -0,0 +1,39 @@
+<?php
+namespace Packagist\WebBundle\Package;
+
+use Composer\Repository\VcsRepository;
+use Composer\Factory;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\IO\BufferIO;
+use PhpAmqpLib\Message\AMQPMessage;
+use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
+
+class BackgroundUpsertConsumer implements ConsumerInterface {
+
+    private $upserter;
+
+    public function __construct(Upserter $upserter)
+    {
+        $this->upserter = $upserter;
+    }
+
+    public function execute(AMQPMessage $message)
+    {
+        $body     = unserialize($message->body);
+        $config   = Factory::createConfig();
+        $output   = new BufferIO('');
+        $output->loadConfiguration($config);
+        try {
+            $this->upserter->execute($body['url'], $body['package_name'], $output);
+        }
+        catch (\Exception $e) {
+            echo $output->getOutput();
+            return ConsumerInterface::MSG_REJECT;
+        }
+        echo $output->getOutput();
+        return serialize(array(
+            'output' => $output->getOutput()
+        ));
+    }
+}
diff --git a/src/Packagist/WebBundle/Package/SymlinkDumper.php b/src/Packagist/WebBundle/Package/SymlinkDumper.php
new file mode 100644
index 0000000..d6fe96d
--- /dev/null
+++ b/src/Packagist/WebBundle/Package/SymlinkDumper.php
@@ -0,0 +1,712 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Package;
+
+use Symfony\Component\Filesystem\Filesystem;
+use Composer\Util\Filesystem as ComposerFilesystem;
+use Symfony\Bridge\Doctrine\RegistryInterface;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Finder\Finder;
+use Packagist\WebBundle\Entity\Version;
+use Doctrine\DBAL\Connection;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class SymlinkDumper
+{
+    /**
+     * Doctrine
+     * @var RegistryInterface
+     */
+    protected $doctrine;
+
+    /**
+     * @var Filesystem
+     */
+    protected $fs;
+
+    /**
+     * @var ComposerFilesystem
+     */
+    protected $cfs;
+
+    /**
+     * @var string
+     */
+    protected $webDir;
+
+    /**
+     * @var string
+     */
+    protected $buildDir;
+
+    /**
+     * @var UrlGeneratorInterface
+     */
+    protected $router;
+
+    /**
+     * Data cache
+     * @var array
+     */
+    private $rootFile;
+
+    /**
+     * Data cache
+     * @var array
+     */
+    private $listings = array();
+
+    /**
+     * Data cache
+     * @var array
+     */
+    private $individualFiles = array();
+
+    /**
+     * Modified times of individual files
+     * @var array
+     */
+    private $individualFilesMtime = array();
+
+    /**
+     * Stores all the disk writes to be replicated in the second build dir after the symlink has been swapped
+     * @var array
+     */
+    private $writeLog = array();
+
+    /**
+     * Generate compressed files.
+     * @var int 0 disabled, 9 maximum.
+     */
+    private $compress;
+
+    /**
+     * Constructor
+     *
+     * @param RegistryInterface     $doctrine
+     * @param Filesystem            $filesystem
+     * @param UrlGeneratorInterface $router
+     * @param string                $webDir     web root
+     * @param string                $targetDir
+     * @param int                   $compress
+     */
+    public function __construct(RegistryInterface $doctrine, Filesystem $filesystem, UrlGeneratorInterface $router, $webDir, $targetDir, $compress)
+    {
+        $this->doctrine = $doctrine;
+        $this->fs = $filesystem;
+        $this->cfs = new ComposerFilesystem;
+        $this->router = $router;
+        $this->webDir = realpath($webDir);
+        $this->buildDir = $targetDir;
+        $this->compress = $compress;
+    }
+
+    /**
+     * Dump a set of packages to the web root
+     *
+     * @param array   $packageIds
+     * @param Boolean $force
+     * @param Boolean $verbose
+     */
+    public function dump(array $packageIds, $force = false, $verbose = false)
+    {
+        $cleanUpOldFiles = date('i') == 0;
+
+        // prepare build dir
+        $webDir = $this->webDir;
+
+        $buildDirA = $this->buildDir.'/a';
+        $buildDirB = $this->buildDir.'/b';
+
+        // initialize
+        $initialRun = false;
+        if (!is_dir($buildDirA) || !is_dir($buildDirB)) {
+            $initialRun = true;
+            if (!$this->removeDirectory($buildDirA) || !$this->removeDirectory($buildDirB)) {
+                throw new \RuntimeException('Failed to delete '.$buildDirA.' or '.$buildDirB);
+            }
+            $this->fs->mkdir($buildDirA);
+            $this->fs->mkdir($buildDirB);
+        }
+
+        // set build dir to the not-active one
+        if (realpath($webDir.'/p') === realpath($buildDirA)) {
+            $buildDir = realpath($buildDirB);
+            $oldBuildDir = realpath($buildDirA);
+        } else {
+            $buildDir = realpath($buildDirA);
+            $oldBuildDir = realpath($buildDirB);
+        }
+
+        // copy existing stuff for smooth BC transition
+        if ($initialRun && !$force) {
+            if (!file_exists($webDir.'/p') || is_link($webDir.'/p')) {
+                @rmdir($buildDir);
+                @rmdir($oldBuildDir);
+                throw new \RuntimeException('Run this again with --force the first time around to make sure it dumps all packages');
+            }
+            if ($verbose) {
+                echo 'Copying existing files'.PHP_EOL;
+            }
+
+            foreach (array($buildDir, $oldBuildDir) as $dir) {
+                $this->cloneDir($webDir.'/p', $dir);
+            }
+        }
+
+        if ($verbose) {
+            echo 'Web dir is '.$webDir.'/p ('.realpath($webDir.'/p').')'.PHP_EOL;
+            echo 'Build dir is '.$buildDir.PHP_EOL;
+        }
+
+        // clean the build dir to start over if we are re-dumping everything
+        if ($force) {
+            // disable the write log since we copy everything at the end in forced mode
+            $this->writeLog = false;
+
+            if ($verbose) {
+                echo 'Cleaning up existing files'.PHP_EOL;
+            }
+            if (!$this->clearDirectory($buildDir)) {
+                return false;
+            }
+        }
+
+        $dumpTimeUpdates = [];
+
+        try {
+            $modifiedIndividualFiles = array();
+
+            $total = count($packageIds);
+            $current = 0;
+            $step = 50;
+            while ($packageIds) {
+                $dumpTime = new \DateTime;
+                $packages = $this->doctrine->getRepository('PackagistWebBundle:Package')->getPackagesWithVersions(array_splice($packageIds, 0, $step));
+
+                if ($verbose) {
+                    echo '['.sprintf('%'.strlen($total).'d', $current).'/'.$total.'] Processing '.$step.' packages'.PHP_EOL;
+                }
+
+                $current += $step;
+
+                // prepare packages in memory
+                foreach ($packages as $package) {
+                    $affectedFiles = array();
+                    $name = strtolower($package->getName());
+
+                    // clean up versions in individual files
+                    if (file_exists($buildDir.'/'.$name.'.files')) {
+                        $files = json_decode(file_get_contents($buildDir.'/'.$name.'.files'));
+
+                        foreach ($files as $file) {
+                            if (substr_count($file, '/') > 1) { // handle old .files with p/*/*.json paths
+                                $file = preg_replace('{^p/}', '', $file);
+                            }
+                            $this->loadIndividualFile($buildDir.'/'.$file, $file);
+                            if (isset($this->individualFiles[$file]['packages'][$name])) {
+                                unset($this->individualFiles[$file]['packages'][$name]);
+                                $modifiedIndividualFiles[$file] = true;
+                            }
+                        }
+                    }
+
+                    // (re)write versions in individual files
+                    foreach ($package->getVersions() as $version) {
+                        foreach (array_slice($version->getNames(), 0, 150) as $versionName) {
+                            if (!preg_match('{^[A-Za-z0-9_-][A-Za-z0-9_.-]*/[A-Za-z0-9_-][A-Za-z0-9_.-]*$}', $versionName) || strpos($versionName, '..')) {
+                                continue;
+                            }
+
+                            $file = $buildDir.'/'.$versionName.'.json';
+                            $key = $versionName.'.json';
+                            $this->dumpVersionToIndividualFile($version, $file, $key);
+                            $modifiedIndividualFiles[$key] = true;
+                            $affectedFiles[$key] = true;
+                        }
+                    }
+
+                    // store affected files to clean up properly in the next update
+                    $this->fs->mkdir(dirname($buildDir.'/'.$name));
+                    $this->writeFile($buildDir.'/'.$name.'.files', json_encode(array_keys($affectedFiles)));
+
+                    $dumpTimeUpdates[$dumpTime->format('Y-m-d H:i:s')][] = $package->getId();
+                }
+
+                unset($packages, $package, $version);
+                $this->doctrine->getManager()->clear();
+
+                if ($current % 250 === 0 || !$packageIds) {
+                    if ($verbose) {
+                        echo 'Dumping individual files'.PHP_EOL;
+                    }
+                    $this->dumpIndividualFiles($buildDir);
+                }
+            }
+
+            // prepare individual files listings
+            if ($verbose) {
+                echo 'Preparing individual files listings'.PHP_EOL;
+            }
+            $safeFiles = array();
+            $individualHashedListings = array();
+            $finder = Finder::create()->files()->ignoreVCS(true)->name('*.json')->in($buildDir)->depth('1');
+
+            foreach ($finder as $file) {
+                // skip hashed files
+                if (strpos($file, '$')) {
+                    continue;
+                }
+
+                $key = basename(dirname($file)).'/'.basename($file);
+                if ($force && !isset($modifiedIndividualFiles[$key])) {
+                    continue;
+                }
+
+                // add hashed provider to listing
+                $listing = $this->getTargetListing($file);
+                $hash = hash_file('sha256', $file);
+                $key = substr($key, 0, -5);
+                $safeFiles[] = $key.'$'.$hash.'.json';
+                $this->listings[$listing]['providers'][$key] = array('sha256' => $hash);
+                $individualHashedListings[$listing] = true;
+            }
+
+            // prepare root file
+            $rootFile = $buildDir.'/packages.json';
+            $this->rootFile = array('packages' => array());
+            $url = $this->router->generate('track_download', array('name' => 'VND/PKG'));
+            $this->rootFile['notify'] = str_replace('VND/PKG', '%package%', $url);
+            $this->rootFile['notify-batch'] = $this->router->generate('track_download_batch');
+            $this->rootFile['providers-url'] = $this->router->generate('home') . 'p/%package%$%hash%.json';
+            $this->rootFile['search'] = $this->router->generate('search', array('_format' => 'json')) . '?q=%query%';
+            $this->rootFile['warning'] = 'Drupal Packagist is deprecated, you should use the official Package Repository from Drupal.org instead. See https://www.drupal.org/node/2822344 for more information.';
+
+            if ($verbose) {
+                echo 'Dumping individual listings'.PHP_EOL;
+            }
+
+            // dump listings to build dir
+            foreach ($individualHashedListings as $listing => $dummy) {
+                list($listingPath, $hash) = $this->dumpListing($buildDir.'/'.$listing);
+                $hashedListing = basename($listingPath);
+                $this->rootFile['provider-includes']['p/'.str_replace($hash, '%hash%', $hashedListing)] = array('sha256' => $hash);
+                $safeFiles[] = $hashedListing;
+            }
+
+            if ($verbose) {
+                echo 'Dumping root'.PHP_EOL;
+            }
+            $this->dumpRootFile($rootFile);
+        } catch (\Exception $e) {
+            // restore files as they were before we started
+            $this->cloneDir($oldBuildDir, $buildDir);
+            throw $e;
+        }
+
+        try {
+            if ($verbose) {
+                echo 'Putting new files in production'.PHP_EOL;
+            }
+
+            // move away old files for BC update
+            if ($initialRun && file_exists($webDir.'/p') && !is_link($webDir.'/p')) {
+                rename($webDir.'/p', $webDir.'/p-old');
+            }
+
+            $this->switchActiveWebDir($webDir, $buildDir);
+        } catch (\Exception $e) {
+            @symlink($oldBuildDir, $webDir.'/p');
+            throw $e;
+        }
+
+        try {
+            if ($initialRun || !is_link($webDir.'/packages.json') || $force) {
+                if ($verbose) {
+                    echo 'Writing/linking the packages.json'.PHP_EOL;
+                }
+                if (file_exists($webDir.'/packages.json')) {
+                    unlink($webDir.'/packages.json');
+                }
+                if (file_exists($webDir.'/packages.json.gz')) {
+                    unlink($webDir.'/packages.json.gz');
+                }
+                if (defined('PHP_WINDOWS_VERSION_BUILD')) {
+                    $sourcePath = $buildDir.'/packages.json';
+                    if (!copy($sourcePath, $webDir.'/packages.json')) {
+                        throw new \RuntimeException('Could not copy the packages.json file');
+                    }
+                } else {
+                    $sourcePath = 'p/packages.json';
+                    if (!symlink($sourcePath, $webDir.'/packages.json')) {
+                        throw new \RuntimeException('Could not symlink the packages.json file');
+                    }
+                    if ($this->compress && !symlink($sourcePath.'.gz', $webDir.'/packages.json.gz')) {
+                        throw new \RuntimeException('Could not symlink the packages.json.gz file');
+                    }
+                }
+            }
+        } catch (\Exception $e) {
+            $this->switchActiveWebDir($webDir, $oldBuildDir);
+            throw $e;
+        }
+
+        // clean up old dir if present on BC update
+        if ($initialRun) {
+            $this->removeDirectory($webDir.'/p-old');
+        }
+
+        // clean the old build dir if we re-dumped everything
+        if ($force) {
+            if ($verbose) {
+                echo 'Cleaning up old build dir'.PHP_EOL;
+            }
+            if (!$this->clearDirectory($oldBuildDir)) {
+                throw new \RuntimeException('Unrecoverable inconsistent state (old build dir could not be cleared), run with --force again to retry');
+            }
+        }
+
+        // copy state to old active dir
+        if ($force) {
+            if ($verbose) {
+                echo 'Copying new contents to old build dir to sync up'.PHP_EOL;
+            }
+            $this->cloneDir($buildDir, $oldBuildDir);
+        } else {
+            if ($verbose) {
+                echo 'Replaying write log in old build dir'.PHP_EOL;
+            }
+            $this->copyWriteLog($buildDir, $oldBuildDir);
+        }
+
+        // clean up old files once an hour
+        if (!$force && $cleanUpOldFiles) {
+            if ($verbose) {
+                echo 'Cleaning up old files'.PHP_EOL;
+            }
+
+            $this->cleanOldFiles($buildDir, $oldBuildDir, $safeFiles);
+        }
+
+        if ($verbose) {
+            echo 'Updating package dump times'.PHP_EOL;
+        }
+        foreach ($dumpTimeUpdates as $dt => $ids) {
+            $retries = 5;
+            // retry loop in case of a lock timeout
+            while ($retries--) {
+                try {
+                    $this->doctrine->getManager()->getConnection()->executeQuery(
+                        'UPDATE package SET dumpedAt=:dumped WHERE id IN (:ids)',
+                        [
+                            'ids' => $ids,
+                            'dumped' => $dt,
+                        ],
+                        ['ids' => Connection::PARAM_INT_ARRAY]
+                    );
+                } catch (\Exception $e) {
+                    if (!$retries) {
+                        throw $e;
+                    }
+                    sleep(2);
+                }
+            }
+        }
+
+        // TODO when a package is deleted, it should be removed from provider files, or marked for removal at least
+        return true;
+    }
+
+    private function switchActiveWebDir($webDir, $buildDir)
+    {
+        $newLink = $webDir.'/p-new';
+        $oldLink = $webDir.'/p';
+
+        if (file_exists($newLink)) {
+            unlink($newLink);
+        }
+        if (!symlink($buildDir, $newLink)) {
+            throw new \RuntimeException('Could not symlink the build dir into the web dir');
+        }
+        if (!rename($newLink, $oldLink)) {
+            throw new \RuntimeException('Could not replace the old symlink with the new one in the web dir');
+        }
+    }
+
+    private function cloneDir($source, $target)
+    {
+        $this->removeDirectory($target);
+        exec('cp -rpf '.escapeshellarg($source).' '.escapeshellarg($target), $output, $exit);
+        if (0 !== $exit) {
+            echo 'Warning, cloning a directory using the php fallback does not keep filemtime, invalid behavior may occur';
+            $this->fs->mirror($source, $target, null, array('override' => true));
+        }
+    }
+
+    private function cleanOldFiles($buildDir, $oldBuildDir, $safeFiles)
+    {
+        $finder = Finder::create()->directories()->ignoreVCS(true)->in($buildDir);
+        foreach ($finder as $vendorDir) {
+            $vendorFiles = Finder::create()->files()->ignoreVCS(true)
+                ->name('/\$[a-f0-9]+\.json$/')
+                ->date('until 10minutes ago')
+                ->in((string) $vendorDir);
+
+            foreach ($vendorFiles as $file) {
+                $key = strtr(str_replace($buildDir.DIRECTORY_SEPARATOR, '', $file), '\\', '/');
+                if (!in_array($key, $safeFiles, true)) {
+                    unlink((string) $file);
+                    if (file_exists($altDirFile = str_replace($buildDir, $oldBuildDir, (string) $file))) {
+                        unlink($altDirFile);
+                    }
+                }
+            }
+        }
+
+        // clean up old provider listings
+        $finder = Finder::create()->depth(0)->files()->name('provider-*.json')->ignoreVCS(true)->in($buildDir)->date('until 10minutes ago');
+        foreach ($finder as $provider) {
+            $key = strtr(str_replace($buildDir.DIRECTORY_SEPARATOR, '', $provider), '\\', '/');
+            if (!in_array($key, $safeFiles, true)) {
+                $path = (string) $provider;
+                unlink($path);
+                if (file_exists($path.'.gz')) {
+                    unlink($path.'.gz');
+                }
+                if (file_exists($altDirFile = str_replace($buildDir, $oldBuildDir, $path))) {
+                    unlink($altDirFile);
+                    if (file_exists($altDirFile.'.gz')) {
+                        unlink($altDirFile.'.gz');
+                    }
+                }
+            }
+        }
+
+        // clean up old root listings
+        $finder = Finder::create()->depth(0)->files()->name('packages.json-*')->ignoreVCS(true)->in($buildDir)->date('until 10minutes ago');
+        foreach ($finder as $rootFile) {
+            $path = (string) $rootFile;
+            unlink($path);
+            if (file_exists($path.'.gz')) {
+                unlink($path.'.gz');
+            }
+            if (file_exists($altDirFile = str_replace($buildDir, $oldBuildDir, $path))) {
+                unlink($altDirFile);
+                if (file_exists($altDirFile.'.gz')) {
+                    unlink($altDirFile.'.gz');
+                }
+            }
+        }
+    }
+
+    private function dumpRootFile($file)
+    {
+        // sort all versions and packages to make sha1 consistent
+        ksort($this->rootFile['packages']);
+        ksort($this->rootFile['provider-includes']);
+        foreach ($this->rootFile['packages'] as $package => $versions) {
+            ksort($this->rootFile['packages'][$package]);
+        }
+
+        if (file_exists($file)) {
+            $timedFile = $file.'-'.time();
+            rename($file, $timedFile);
+            if (file_exists($file.'.gz')) {
+                rename($file.'.gz', $timedFile.'.gz');
+            }
+        }
+
+        $json = json_encode($this->rootFile);
+        $time = time();
+
+        $this->writeFile($file, $json, $time);
+        if ($this->compress) {
+            $this->writeFile($file . '.gz', gzencode($json, $this->compress), $time);
+        }
+    }
+
+    private function dumpListing($path)
+    {
+        $key = basename($path);
+
+        // sort files to make hash consistent
+        ksort($this->listings[$key]['providers']);
+
+        $json = json_encode($this->listings[$key]);
+        $hash = hash('sha256', $json);
+        $path = substr($path, 0, -5) . '$' . $hash . '.json';
+        $time = time();
+
+        if (!file_exists($path)) {
+            $this->writeFile($path, $json, $time);
+            if ($this->compress) {
+                $this->writeFile($path . '.gz', gzencode($json, $this->compress), $time);
+            }
+        }
+
+        return array($path, $hash);
+    }
+
+    private function loadIndividualFile($path, $key)
+    {
+        if (isset($this->individualFiles[$key])) {
+            return;
+        }
+
+        if (file_exists($path)) {
+            $this->individualFiles[$key] = json_decode(file_get_contents($path), true);
+            $this->individualFilesMtime[$key] = filemtime($path);
+        } else {
+            $this->individualFiles[$key] = array();
+            $this->individualFilesMtime[$key] = 0;
+        }
+    }
+
+    private function dumpIndividualFiles($buildDir)
+    {
+        // dump individual files to build dir
+        foreach ($this->individualFiles as $file => $dummy) {
+            $this->dumpIndividualFile($buildDir.'/'.$file, $file);
+        }
+
+        $this->individualFiles = array();
+        $this->individualFilesMtime = array();
+    }
+
+    private function dumpIndividualFile($path, $key)
+    {
+        // sort all versions and packages to make sha1 consistent
+        ksort($this->individualFiles[$key]['packages']);
+        foreach ($this->individualFiles[$key]['packages'] as $package => $versions) {
+            ksort($this->individualFiles[$key]['packages'][$package]);
+        }
+
+        $this->fs->mkdir(dirname($path));
+
+        $json = json_encode($this->individualFiles[$key]);
+        $this->writeFile($path, $json, $this->individualFilesMtime[$key]);
+
+        // write the hashed provider file
+        $hashedFile = substr($path, 0, -5) . '$' . hash('sha256', $json) . '.json';
+        $this->writeFile($hashedFile, $json);
+    }
+
+    private function dumpVersionToIndividualFile(Version $version, $file, $key)
+    {
+        $this->loadIndividualFile($file, $key);
+        $data = $version->toArray();
+        $data['uid'] = $version->getId();
+        $this->individualFiles[$key]['packages'][strtolower($version->getName())][$version->getVersion()] = $data;
+        $timestamp = $version->getReleasedAt() ? $version->getReleasedAt()->getTimestamp() : time();
+        if (!isset($this->individualFilesMtime[$key]) || $this->individualFilesMtime[$key] < $timestamp) {
+            $this->individualFilesMtime[$key] = $timestamp;
+        }
+    }
+
+    private function clearDirectory($path)
+    {
+        if (!$this->removeDirectory($path)) {
+            echo 'Could not remove the build dir entirely, aborting';
+
+            return false;
+        }
+        $this->fs->mkdir($path);
+        return true;
+    }
+
+    private function removeDirectory($path)
+    {
+        $retries = 5;
+        do {
+            if (!$this->cfs->removeDirectory($path)) {
+                usleep(200);
+            }
+            clearstatcache();
+        } while (is_dir($path) && $retries--);
+
+        return !is_dir($path);
+    }
+
+    private function getTargetListingBlocks($now)
+    {
+        $blocks = array();
+
+        // monday last week
+        $blocks['latest'] = strtotime('monday last week', $now);
+
+        $month = date('n', $now);
+        $month = ceil($month / 3) * 3 - 2; // 1 for months 1-3, 10 for months 10-12
+        $block = new \DateTime(date('Y', $now).'-'.$month.'-01'); // 1st day of current trimester
+
+        // split last 12 months in 4 trimesters
+        for ($i=0; $i < 4; $i++) {
+            $blocks[$block->format('Y-m')] = $block->getTimestamp();
+            $block->sub(new \DateInterval('P3M'));
+        }
+
+        $year = (int) $block->format('Y');
+
+        while ($year >= 2013) {
+            $blocks[''.$year] = strtotime($year.'-01-01');
+            $year--;
+        }
+
+        return $blocks;
+    }
+
+    private function getTargetListing($file)
+    {
+        static $blocks;
+
+        if (!$blocks) {
+            $blocks = $this->getTargetListingBlocks(time());
+        }
+
+        $mtime = filemtime($file);
+
+        foreach ($blocks as $label => $block) {
+            if ($mtime >= $block) {
+                return "provider-${label}.json";
+            }
+        }
+
+        return "provider-archived.json";
+    }
+
+    private function writeFile($path, $contents, $mtime = null)
+    {
+        file_put_contents($path, $contents);
+        if ($mtime !== null) {
+            touch($path, $mtime);
+        }
+
+        if (is_array($this->writeLog)) {
+            $this->writeLog[$path] = array($contents, $mtime);
+        }
+    }
+
+    private function copyWriteLog($from, $to)
+    {
+        foreach ($this->writeLog as $path => $op) {
+            $path = str_replace($from, $to, $path);
+
+            $this->fs->mkdir(dirname($path));
+            file_put_contents($path, $op[0]);
+            if ($op[1] !== null) {
+                touch($path, $op[1]);
+            }
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/Package/Updater.php b/src/Packagist/WebBundle/Package/Updater.php
new file mode 100644
index 0000000..eea228a
--- /dev/null
+++ b/src/Packagist/WebBundle/Package/Updater.php
@@ -0,0 +1,503 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Package;
+
+use Composer\Package\AliasPackage;
+use Composer\Package\PackageInterface;
+use Composer\Repository\RepositoryInterface;
+use Composer\Repository\InvalidRepositoryException;
+use Composer\Util\ErrorHandler;
+use Composer\Util\RemoteFilesystem;
+use Composer\Json\JsonFile;
+use Composer\Config;
+use Composer\IO\IOInterface;
+use Packagist\WebBundle\Entity\Author;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\Tag;
+use Packagist\WebBundle\Entity\Version;
+use Packagist\WebBundle\Entity\SuggestLink;
+use Symfony\Bridge\Doctrine\RegistryInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Updater
+{
+    const UPDATE_EQUAL_REFS = 1;
+    const DELETE_BEFORE = 2;
+
+    /**
+     * Doctrine
+     * @var RegistryInterface
+     */
+    protected $doctrine;
+
+    /**
+     * Supported link types
+     * @var array
+     */
+    protected $supportedLinkTypes = array(
+        'require'     => array(
+            'method' => 'getRequires',
+            'entity' => 'RequireLink',
+        ),
+        'conflict'    => array(
+            'method' => 'getConflicts',
+            'entity' => 'ConflictLink',
+        ),
+        'provide'     => array(
+            'method' => 'getProvides',
+            'entity' => 'ProvideLink',
+        ),
+        'replace'     => array(
+            'method' => 'getReplaces',
+            'entity' => 'ReplaceLink',
+        ),
+        'devRequire' => array(
+            'method' => 'getDevRequires',
+            'entity' => 'DevRequireLink',
+        ),
+    );
+
+    /**
+     * Constructor
+     *
+     * @param RegistryInterface $doctrine
+     */
+    public function __construct(RegistryInterface $doctrine)
+    {
+        $this->doctrine = $doctrine;
+
+        ErrorHandler::register();
+    }
+
+    /**
+     * Update a project
+     *
+     * @param \Packagist\WebBundle\Entity\Package $package
+     * @param RepositoryInterface $repository the repository instance used to update from
+     * @param int $flags a few of the constants of this class
+     * @param \DateTime $start
+     */
+    public function update(IOInterface $io, Config $config, Package $package, RepositoryInterface $repository, $flags = 0, \DateTime $start = null)
+    {
+        $rfs = new RemoteFilesystem($io, $config);
+        $blacklist = '{^symfony/symfony (2.0.[456]|dev-charset|dev-console)}i';
+
+        if (null === $start) {
+            $start = new \DateTime();
+        }
+        $pruneDate = clone $start;
+        $pruneDate->modify('-1min');
+
+        $versions = $repository->getPackages();
+        $em = $this->doctrine->getManager();
+
+        usort($versions, function ($a, $b) {
+            $aVersion = $a->getVersion();
+            $bVersion = $b->getVersion();
+            if ($aVersion === '9999999-dev' || 'dev-' === substr($aVersion, 0, 4)) {
+                $aVersion = 'dev';
+            }
+            if ($bVersion === '9999999-dev' || 'dev-' === substr($bVersion, 0, 4)) {
+                $bVersion = 'dev';
+            }
+            $aIsDev = $aVersion === 'dev' || substr($aVersion, -4) === '-dev';
+            $bIsDev = $bVersion === 'dev' || substr($bVersion, -4) === '-dev';
+
+            // push dev versions to the end
+            if ($aIsDev !== $bIsDev) {
+                return $aIsDev ? 1 : -1;
+            }
+
+            // equal versions are sorted by date
+            if ($aVersion === $bVersion) {
+                return $a->getReleaseDate() > $b->getReleaseDate() ? 1 : -1;
+            }
+
+            // the rest is sorted by version
+            return version_compare($aVersion, $bVersion);
+        });
+
+        $versionRepository = $this->doctrine->getRepository('PackagistWebBundle:Version');
+
+        if ($flags & self::DELETE_BEFORE) {
+            foreach ($package->getVersions() as $version) {
+                $versionRepository->remove($version);
+            }
+
+            $em->flush();
+            $em->refresh($package);
+        }
+
+        $lastUpdated = true;
+        foreach ($versions as $version) {
+            if ($version instanceof AliasPackage) {
+                continue;
+            }
+
+            if (preg_match($blacklist, $version->getName().' '.$version->getPrettyVersion())) {
+                continue;
+            }
+
+            $lastUpdated = $this->updateInformation($package, $version, $flags);
+            if ($lastUpdated) {
+                $em->flush();
+            }
+        }
+
+        if (!$lastUpdated) {
+            $em->flush();
+        }
+
+        // remove outdated versions
+        foreach ($package->getVersions() as $version) {
+            if ($version->getUpdatedAt() < $pruneDate) {
+                $versionRepository->remove($version);
+            }
+        }
+
+        if (preg_match('{^(?:git://|git@|https?://)github.com[:/]([^/]+)/(.+?)(?:\.git|/)?$}i', $package->getRepository(), $match)) {
+            $this->updateGitHubInfo($rfs, $package, $match[1], $match[2]);
+        }
+
+        $package->setUpdatedAt(new \DateTime);
+        $package->setCrawledAt(new \DateTime);
+        $em->flush();
+        if ($repository->hadInvalidBranches()) {
+            throw new InvalidRepositoryException('Some branches contained invalid data and were discarded, it is advised to review the log and fix any issues present in branches');
+        }
+    }
+
+    private function updateInformation(Package $package, PackageInterface $data, $flags)
+    {
+        $em = $this->doctrine->getManager();
+        $version = new Version();
+
+        $normVersion = $data->getVersion();
+
+        $existingVersion = $package->getVersion($normVersion);
+        if ($existingVersion) {
+            $source = $existingVersion->getSource();
+            // update if the right flag is set, or the source reference has changed (re-tag or new commit on branch)
+            if ($source['reference'] !== $data->getSourceReference() || ($flags & self::UPDATE_EQUAL_REFS)) {
+                $version = $existingVersion;
+            } else {
+                // mark it updated to avoid it being pruned
+                $existingVersion->setUpdatedAt(new \DateTime);
+
+                return false;
+            }
+        }
+
+        $version->setName($package->getName());
+        $version->setVersion($data->getPrettyVersion());
+        $version->setNormalizedVersion($normVersion);
+        $version->setDevelopment($data->isDev());
+
+        $em->persist($version);
+
+        $descr = $this->sanitize($data->getDescription());
+        $version->setDescription($descr);
+        $package->setDescription($descr);
+        $version->setHomepage($data->getHomepage());
+        $version->setLicense($data->getLicense() ?: array());
+
+        $version->setPackage($package);
+        $version->setUpdatedAt(new \DateTime);
+        $version->setReleasedAt($data->getReleaseDate());
+
+        if ($data->getSourceType()) {
+            $source['type'] = $data->getSourceType();
+            $source['url'] = $data->getSourceUrl();
+            $source['reference'] = $data->getSourceReference();
+            $version->setSource($source);
+        } else {
+            $version->setSource(null);
+        }
+
+        if ($data->getDistType()) {
+            $dist['type'] = $data->getDistType();
+            $dist['url'] = $data->getDistUrl();
+            $dist['reference'] = $data->getDistReference();
+            $dist['shasum'] = $data->getDistSha1Checksum();
+            $version->setDist($dist);
+        } else {
+            $version->setDist(null);
+        }
+
+        if ($data->getType()) {
+            $type = $this->sanitize($data->getType());
+            $version->setType($type);
+            if ($type !== $package->getType()) {
+                $package->setType($type);
+            }
+        }
+
+        $version->setTargetDir($data->getTargetDir());
+        $version->setAutoload($data->getAutoload());
+        $version->setExtra($data->getExtra());
+        $version->setBinaries($data->getBinaries());
+        $version->setIncludePaths($data->getIncludePaths());
+        $version->setSupport($data->getSupport());
+
+        $version->getTags()->clear();
+        if ($data->getKeywords()) {
+            $keywords = array();
+            foreach ($data->getKeywords() as $keyword) {
+                $keywords[mb_strtolower($keyword, 'UTF-8')] = $keyword;
+            }
+            foreach ($keywords as $keyword) {
+                $tag = Tag::getByName($em, $keyword, true);
+                if (!$version->getTags()->contains($tag)) {
+                    $version->addTag($tag);
+                }
+            }
+        }
+
+        $authorRepository = $this->doctrine->getRepository('PackagistWebBundle:Author');
+
+        $version->getAuthors()->clear();
+        if ($data->getAuthors()) {
+            foreach ($data->getAuthors() as $authorData) {
+                $author = null;
+
+                foreach (array('email', 'name', 'homepage', 'role') as $field) {
+                    if (isset($authorData[$field])) {
+                        $authorData[$field] = trim($authorData[$field]);
+                        if ('' === $authorData[$field]) {
+                            $authorData[$field] = null;
+                        }
+                    } else {
+                        $authorData[$field] = null;
+                    }
+                }
+
+                // skip authors with no information
+                if (!isset($authorData['email']) && !isset($authorData['name'])) {
+                    continue;
+                }
+
+                $author = $authorRepository->findOneBy(array(
+                    'email' => $authorData['email'],
+                    'name' => $authorData['name'],
+                    'homepage' => $authorData['homepage'],
+                    'role' => $authorData['role'],
+                ));
+
+                if (!$author) {
+                    $author = new Author();
+                    $em->persist($author);
+                }
+
+                foreach (array('email', 'name', 'homepage', 'role') as $field) {
+                    if (isset($authorData[$field])) {
+                        $author->{'set'.$field}($authorData[$field]);
+                    }
+                }
+
+                $author->setUpdatedAt(new \DateTime);
+                if (!$version->getAuthors()->contains($author)) {
+                    $version->addAuthor($author);
+                }
+                if (!$author->getVersions()->contains($version)) {
+                    $author->addVersion($version);
+                }
+            }
+        }
+
+        // handle links
+        foreach ($this->supportedLinkTypes as $linkType => $opts) {
+            $links = array();
+            foreach ($data->{$opts['method']}() as $link) {
+                $constraint = $link->getPrettyConstraint();
+                if (false !== strpos($constraint, ',') && false !== strpos($constraint, '@')) {
+                    $constraint = preg_replace_callback('{([><]=?\s*[^@]+?)@([a-z]+)}i', function ($matches) {
+                        if ($matches[2] === 'stable') {
+                            return $matches[1];
+                        }
+
+                        return $matches[1].'-'.$matches[2];
+                    }, $constraint);
+                }
+
+                $links[$link->getTarget()] = $constraint;
+            }
+
+            foreach ($version->{'get'.$linkType}() as $link) {
+                // clear links that have changed/disappeared (for updates)
+                if (!isset($links[$link->getPackageName()]) || $links[$link->getPackageName()] !== $link->getPackageVersion()) {
+                    $version->{'get'.$linkType}()->removeElement($link);
+                    $em->remove($link);
+                } else {
+                    // clear those that are already set
+                    unset($links[$link->getPackageName()]);
+                }
+            }
+
+            foreach ($links as $linkPackageName => $linkPackageVersion) {
+                $class = 'Packagist\WebBundle\Entity\\'.$opts['entity'];
+                $link = new $class;
+                $link->setPackageName($linkPackageName);
+                $link->setPackageVersion($linkPackageVersion);
+                $version->{'add'.$linkType.'Link'}($link);
+                $link->setVersion($version);
+                $em->persist($link);
+            }
+        }
+
+        // handle suggests
+        if ($suggests = $data->getSuggests()) {
+            foreach ($version->getSuggest() as $link) {
+                // clear links that have changed/disappeared (for updates)
+                if (!isset($suggests[$link->getPackageName()]) || $suggests[$link->getPackageName()] !== $link->getPackageVersion()) {
+                    $version->getSuggest()->removeElement($link);
+                    $em->remove($link);
+                } else {
+                    // clear those that are already set
+                    unset($suggests[$link->getPackageName()]);
+                }
+            }
+
+            foreach ($suggests as $linkPackageName => $linkPackageVersion) {
+                $link = new SuggestLink;
+                $link->setPackageName($linkPackageName);
+                $link->setPackageVersion($linkPackageVersion);
+                $version->addSuggestLink($link);
+                $link->setVersion($version);
+                $em->persist($link);
+            }
+        } elseif (count($version->getSuggest())) {
+            // clear existing suggests if present
+            foreach ($version->getSuggest() as $link) {
+                $em->remove($link);
+            }
+            $version->getSuggest()->clear();
+        }
+
+        if (!$package->getVersions()->contains($version)) {
+            $package->addVersions($version);
+        }
+
+        return true;
+    }
+
+    private function updateGitHubInfo(RemoteFilesystem $rfs, Package $package, $owner, $repo)
+    {
+        $baseApiUrl = 'https://api.github.com/repos/'.$owner.'/'.$repo;
+
+        try {
+            $repoData = JsonFile::parseJson($rfs->getContents('github.com', $baseApiUrl, false), $baseApiUrl);
+        } catch (\Exception $e) {
+            return;
+        }
+
+        try {
+            $opts = ['http' => ['header' => ['Accept: application/vnd.github.v3.html']]];
+            $readme = $rfs->getContents('github.com', $baseApiUrl.'/readme', false, $opts);
+        } catch (\Exception $e) {
+            if (!$e instanceof \Composer\Downloader\TransportException || $e->getCode() !== 404) {
+                return;
+            }
+            // 404s just mean no readme present so we proceed with the rest
+        }
+
+        if (!empty($readme)) {
+            $elements = array(
+                'p',
+                'br',
+                'small',
+                'strong', 'b',
+                'em', 'i',
+                'strike',
+                'sub', 'sup',
+                'ins', 'del',
+                'ol', 'ul', 'li',
+                'h1', 'h2', 'h3',
+                'dl', 'dd', 'dt',
+                'pre', 'code', 'samp', 'kbd',
+                'q', 'blockquote', 'abbr', 'cite',
+                'table', 'thead', 'tbody', 'th', 'tr', 'td',
+                'a[href|target|rel|id]',
+                'img[src|title|alt|width|height|style]'
+            );
+            $config = \HTMLPurifier_Config::createDefault();
+            $config->set('HTML.Allowed', implode(',', $elements));
+            $config->set('Attr.EnableID', true);
+            $config->set('Attr.AllowedFrameTargets', ['_blank']);
+            $purifier = new \HTMLPurifier($config);
+            $readme = $purifier->purify($readme);
+
+            $dom = new \DOMDocument();
+            $dom->loadHTML('<?xml encoding="UTF-8">' . $readme);
+
+            // Links can not be trusted, mark them nofollow and convert relative to absolute links
+            $links = $dom->getElementsByTagName('a');
+            foreach ($links as $link) {
+                $link->setAttribute('rel', 'nofollow');
+                if ('#' === substr($link->getAttribute('href'), 0, 1)) {
+                    $link->setAttribute('href', '#user-content-'.substr($link->getAttribute('href'), 1));
+                } elseif (false === strpos($link->getAttribute('href'), '//')) {
+                    $link->setAttribute('href', 'https://github.com/'.$owner.'/'.$repo.'/blob/HEAD/'.$link->getAttribute('href'));
+                }
+            }
+
+            // convert relative to absolute images
+            $images = $dom->getElementsByTagName('img');
+            foreach ($images as $img) {
+                if (false === strpos($img->getAttribute('src'), '//')) {
+                    $img->setAttribute('src', 'https://raw.github.com/'.$owner.'/'.$repo.'/HEAD/'.$img->getAttribute('src'));
+                }
+            }
+
+            // remove first title as it's usually the project name which we don't need
+            if ($dom->getElementsByTagName('h1')->length) {
+                $first = $dom->getElementsByTagName('h1')->item(0);
+                $first->parentNode->removeChild($first);
+            } elseif ($dom->getElementsByTagName('h2')->length) {
+                $first = $dom->getElementsByTagName('h2')->item(0);
+                $first->parentNode->removeChild($first);
+            }
+
+            $readme = $dom->saveHTML();
+            $readme = substr($readme, strpos($readme, '<body>')+6);
+            $readme = substr($readme, 0, strrpos($readme, '</body>'));
+
+            $package->setReadme($readme);
+        }
+
+        if (!empty($repoData['language'])) {
+            $package->setLanguage($repoData['language']);
+        }
+        if (isset($repoData['stargazers_count'])) {
+            $package->setGitHubStars($repoData['stargazers_count']);
+        }
+        if (isset($repoData['subscribers_count'])) {
+            $package->setGitHubWatches($repoData['subscribers_count']);
+        }
+        if (isset($repoData['network_count'])) {
+            $package->setGitHubForks($repoData['network_count']);
+        }
+        if (isset($repoData['open_issues_count'])) {
+            $package->setGitHubOpenIssues($repoData['open_issues_count']);
+        }
+    }
+
+    private function sanitize($str)
+    {
+        // remove escape chars
+        $str = preg_replace("{\x1B(?:\[.)?}u", '', $str);
+
+        return preg_replace("{[\x01-\x1A]}u", '', $str);
+    }
+}
diff --git a/src/Packagist/WebBundle/Package/Upserter.php b/src/Packagist/WebBundle/Package/Upserter.php
new file mode 100644
index 0000000..db0fa61
--- /dev/null
+++ b/src/Packagist/WebBundle/Package/Upserter.php
@@ -0,0 +1,96 @@
+<?php
+namespace Packagist\WebBundle\Package;
+
+use Composer\Repository\InvalidRepositoryException;
+use Composer\Repository\VcsRepository;
+use Composer\Factory;
+use Composer\Package\Loader\ValidatingArrayLoader;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\IO\BufferIO;
+use Packagist\WebBundle\Entity\Package;
+use PhpAmqpLib\Message\AMQPMessage;
+use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
+use Drupal\ParseComposer\ReleaseInfoFactory;
+
+class Upserter {
+
+    private $container;
+    private $doctrine;
+    private $router;
+
+    public function __construct($doctrine, $router, $updater)
+    {
+        $this->doctrine = $doctrine;
+        $this->router   = $router;
+        $this->updater  = $updater;
+    }
+
+    public function execute($url, $packageName, BufferIO $output)
+    {
+        $config = Factory::createConfig();
+        $packageRepository = $this->doctrine
+            ->getRepository('PackagistWebBundle:Package');
+        $response = false;
+        $em = $this->doctrine->getManager();
+        // PackageRepository::packageExists() uses too much caching for us.
+        $res = $em->createQuery("SELECT p.name FROM Packagist\WebBundle\Entity\Package p WHERE p.name = :name")
+            ->setParameters(['name' => $packageName])
+            ->getResult();
+        if (!empty($res)) {
+            $output->write("{$packageName} already exists.");
+            return;
+        }
+        $output->write("adding $packageName");
+        $package = new Package();
+        $package->setRepository($url);
+        $package->setName($packageName);
+        $em->persist($package);
+        $em->flush();
+        $releaseInfoFactory = new ReleaseInfoFactory();
+        $releases = $releaseInfoFactory
+            ->getReleaseInfo($packageName, [7, 8]);
+        if (empty($releases)) {
+            $output->write($err = "no valid releases for {$packageName}");
+            throw new \Exception($err);
+        }
+        $package  = $packageRepository->getPackageByName($packageName);
+        $loader   = new ValidatingArrayLoader(new ArrayLoader());
+        try {
+            $repository = new VcsRepository(
+                array('url' => $package->getRepository()),
+                $output,
+                $config
+            );
+            $repository->setLoader($loader);
+            $output->write("Updating $packageName");
+            $this->updater->update(
+                $output,
+                $config,
+                $package,
+                $repository
+            );
+            $output->write('Updated '.$package->getName());
+        }
+        catch (InvalidRepositoryException $e) {
+            $output->write(
+                '<error>Broken repository in '
+                .$this->router->generate(
+                    'view_package',
+                    array('name' => $package->getName())
+                    , true
+                ).': '.$e->getMessage().'</error>'
+            );
+            throw $e;
+        }
+        catch (\Exception $e) {
+            $output->write(
+                '<error>Error updating '.$this->router->generate(
+                    'view_package',
+                    array('name' => $package->getName()),
+                    true
+                ).' ['.get_class($e).']: '.$e->getMessage().' at '
+                .$e->getFile().':'.$e->getLine().'</error>');
+            throw $e;
+        }
+    }
+}
diff --git a/src/Packagist/WebBundle/PackagistWebBundle.php b/src/Packagist/WebBundle/PackagistWebBundle.php
new file mode 100644
index 0000000..1f12bcf
--- /dev/null
+++ b/src/Packagist/WebBundle/PackagistWebBundle.php
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class PackagistWebBundle extends Bundle
+{
+
+}
diff --git a/src/Packagist/WebBundle/Resources/config/services.yml b/src/Packagist/WebBundle/Resources/config/services.yml
new file mode 100644
index 0000000..9a47cfb
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/config/services.yml
@@ -0,0 +1,127 @@
+services:
+    packagist.twig.extension:
+        class: Packagist\WebBundle\Twig\PackagistExtension
+        arguments: [ '@doctrine' ]
+        tags:
+            - { name: twig.extension }
+
+    twig.extension.text:
+        class: Twig_Extensions_Extension_Text
+        tags:
+            - { name: twig.extension }
+
+    packagist.cache_listener:
+        class: Packagist\WebBundle\EventListener\CacheListener
+        tags:
+            - { name: kernel.event_listener, event: kernel.response, method: onResponse }
+
+    packagist.registration_listener:
+        class: Packagist\WebBundle\EventListener\RegistrationListener
+        arguments: [ '@fos_user.util.token_generator' ]
+        tags:
+            - { name: kernel.event_subscriber }
+
+    packagist.package_dumper:
+        class: Packagist\WebBundle\Package\SymlinkDumper
+        arguments: [ '@doctrine', '@filesystem', '@router', '%kernel.root_dir%/../web/', '%packagist_metadata_dir%', '%packagist_dumper_compress%' ]
+
+    packagist.user_provider:
+        class: Packagist\WebBundle\Security\Provider\UserProvider
+        public: false
+        arguments: ['@fos_user.user_manager', '@fos_user.user_provider.username_email']
+
+    packagist.user_repository:
+        class: Packagist\WebBundle\Entity\UserRepository
+        factory: ['@doctrine', getRepository]
+        arguments: ['PackagistWebBundle:User']
+
+    packagist.package_repository:
+        class: Packagist\WebBundle\Entity\PackageRepository
+        factory: ['@doctrine', getRepository]
+        arguments: ['PackagistWebBundle:Package']
+
+    packagist.package_updater:
+        class: DrupalPackagist\Bundle\Package\Updater
+        arguments: ['@doctrine']
+
+    fos_user.util.user_manipulator:
+        class: Packagist\WebBundle\Util\UserManipulator
+        arguments: ['@fos_user.user_manager', '@fos_user.util.token_generator']
+
+    packagist.oauth.registration_form_handler:
+        class: Packagist\WebBundle\Form\Handler\OAuthRegistrationFormHandler
+        arguments: ['@fos_user.user_manager', '@fos_user.util.token_generator']
+
+    packagist.oauth.registration_form_type:
+        class: Packagist\WebBundle\Form\Type\OAuthRegistrationFormType
+        tags:
+            - { name: form.type, alias: packagist_oauth_user_registration }
+
+    packagist.oauth.registration_form:
+        factory: ['@form.factory', create]
+        class: Symfony\Component\Form\Form
+        arguments:
+            - 'packagist_oauth_user_registration'
+
+    packagist.download_manager:
+        class: Packagist\WebBundle\Model\DownloadManager
+        arguments:
+            - '@snc_redis.default_client'
+
+    packagist.favorite_manager:
+        class: Packagist\WebBundle\Model\FavoriteManager
+        arguments:
+            - '@snc_redis.default_client'
+            - '@packagist.package_repository'
+            - '@packagist.user_repository'
+
+    packagist.package_manager:
+        class: Packagist\WebBundle\Model\PackageManager
+        arguments:
+            - '@doctrine.orm.entity_manager'
+            - '@mailer'
+            - '@twig'
+            - '@logger'
+            - { from: %mailer_from_email%, fromName: %mailer_from_name% }
+
+    packagist.profile.form.type:
+        class: Packagist\WebBundle\Form\Type\ProfileFormType
+        arguments: [%fos_user.model.user.class%]
+        tags:
+            - { name: form.type, alias: packagist_user_profile }
+
+    packagist.menu_builder:
+        class: Packagist\WebBundle\Menu\MenuBuilder
+        arguments: ['@knp_menu.factory', '@security.token_storage']
+
+    packagist.menu.user:
+        class: Knp\Menu\MenuItem
+        factory: ['@packagist.menu_builder', createUserMenu]
+        tags:
+            - { name: knp_menu.menu, alias: user_menu }
+
+    packagist.menu.profile:
+        class: Knp\Menu\MenuItem
+        factory: ['@packagist.menu_builder', createProfileMenu]
+        tags:
+            - { name: knp_menu.menu, alias: profile_menu }
+
+    packagist.package_upserter:
+        class: Packagist\WebBundle\Package\Upserter
+        arguments:
+            - @doctrine
+            - @router
+            - @packagist.package_updater
+
+    packagist.background_package_updater_consumer:
+        class: Packagist\WebBundle\Package\BackgroundUpdaterConsumer
+        arguments:
+            - @service_container
+
+    packagist.background_package_upsert_consumer:
+        class: Packagist\WebBundle\Package\BackgroundUpsertConsumer
+        arguments:
+            - @packagist.package_upserter
+
+parameters:
+    security.exception_listener.class: Packagist\WebBundle\Security\ExceptionListener
diff --git a/src/Packagist/WebBundle/Resources/public/css/main.css b/src/Packagist/WebBundle/Resources/public/css/main.css
new file mode 100644
index 0000000..d8fbe29
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/public/css/main.css
@@ -0,0 +1,1563 @@
+@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300,500,600,300italic);
+
+/*
+ main header: #2d2d32
+ secondary header: #f28d1a
+
+ light grey: #f3f4f5;
+ colors danger/error: #ff4533 #cd3729
+ colors warning: #ed9e2e #F28D1A
+ colors success: #83c129 #69ad21
+ colors default: #bbbfc4 #a5aab0
+
+ */
+
+@font-face {
+    font-family: 'fontello';
+    src: url('../font/fontello.eot');
+    src: url('../font/fontello.eot?#iefix') format('embedded-opentype'),
+    url('../font/fontello.woff') format('woff'),
+    url('../font/fontello.ttf') format('truetype'),
+    url('../font/fontello.svg#fontello') format('svg');
+    font-weight: normal;
+    font-style: normal;
+}
+
+html, body {
+    height: 100%;
+}
+
+pre, code {
+    background: #fff;
+    font-family: "Source Code Pro", Menlo, Consolas, "Courier New", Courier, monospace;
+    border-radius: 0;
+    border: none;
+    color: #2d2d32;
+    line-height: 12px;
+    font-size: 12px;
+}
+pre {
+  border-left: 2px solid #2d2d32;
+}
+
+label {
+  display: block;
+  font-weight: normal;
+}
+
+body {
+    background: #FAFAFA;
+    font-family: 'Open Sans', sans-serif;
+    padding-top: 58px;
+}
+.logo {
+  position: absolute;
+  width: 100px;
+  left: -98px;
+  top: -43px;
+}
+@media (min-width: 1200px) {
+  .logo {
+    width: 80px;
+    left: -90px;
+  }
+}
+
+hr {
+  border-color: #F28D1A;
+}
+a, .package .package-aside a {
+  color: #B34E22;
+}
+a:hover, .package .package-aside a:hover {
+  color: #2d2d32;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  font-family: 'Open Sans', sans-serif;
+  font-weight: 300;
+}
+i, cite, em, var, address, dfn {
+  font-style: italic;
+  font-weight: 300;
+}
+strong {
+  font-weight: 600;
+}
+.h3, h3 {
+    font-size: 16px;
+    font-weight: 600;
+}
+
+.navbar-wrapper {
+    left: 0;
+    right: 0;
+    top: 0;
+    min-height: 58px;
+    z-index: 15;
+    background: #2d2d32;
+}
+
+.navbar {
+    margin: 0;
+    border: 0;
+    -webkit-font-smoothing: subpixel-antialiased;
+}
+
+.navbar .navbar-brand {
+    padding: 12px 4px;
+    line-height: 32px;
+    margin: 0;
+}
+
+.navbar .navbar-brand a {
+    float: left;
+    font-size: 24px;
+    font-weight: 600;
+    color: #fff;
+    text-decoration: none;
+}
+
+.navbar .navbar-brand em {
+    padding-left: 15px;
+    padding-top: 8px;
+    font-size: 14px;
+    color: #f3f4f5;
+}
+
+.navbar-collapse {
+    border: 0;
+}
+
+.navbar-toggle {
+    background: rgba(0, 0, 0, 0.3);
+    margin-top: 12px;
+}
+
+.navbar-toggle span {
+    background: #fff;
+}
+
+.navbar .nav > li > a, .navbar .nav > .active > a, .navbar .nav > li > section {
+    padding: 18px 25px 19px;
+    background: none;
+    box-shadow: none;
+    text-shadow: none;
+    color: #fff;
+}
+.nav .nav-user-signin {
+    padding: 18px 25px 19px !important;
+}
+
+.navbar .nav > .active > a, .navbar .nav > .active > a:hover, .navbar .nav > .active > a:focus {
+    box-shadow: none;
+    color: #fff;
+}
+
+
+.navbar .nav > li > a:hover {
+    background: rgba(255, 255, 255, 0.1);
+    color: #fff;
+}
+
+.navbar .nav > li > a:focus {
+    background: #f28d1a;
+    color: #fff;
+}
+
+.navbar .nav {
+    float: right;
+    margin: 1px 0 0 0;
+    color: #fff;
+    font-family: 'Open Sans', sans-serif;
+    font-size: 14px;
+}
+
+.nav-user {
+    position: relative;
+}
+
+.navbar .nav > li > a.nav-signout {
+    padding: 19px 19px 15px;
+    font-size: 22px;
+}
+
+.navbar .nav > .nav-user > section {
+    cursor: pointer;
+}
+
+.navbar .nav > .nav-user > .nav-user-signin {
+    padding: 17px 20px;
+}
+
+.navbar .nav > .nav-user > section > .nav-user-menu, .navbar .nav > .nav-user > section > .signin-box {
+    display: none;
+}
+
+.navbar .nav > .nav-user > section:hover > .nav-user-menu, .navbar .nav > .nav-user > section:hover > .signin-box {
+    display: block;
+}
+
+@media (max-width: 767px) {
+    .navbar .nav { float: none; }
+    .navbar .nav > li > a,
+    .navbar .nav > .active > a {
+        border-bottom: 1px solid rgba(118, 111, 107, 0.69);
+    }
+    .navbar .nav > li:last-child > a {
+        border-bottom: 0;
+    }
+    .navbar .nav > .nav-user > section:hover > .nav-user-menu,
+    .navbar .nav > .nav-user > .nav-user-signin:hover > .signin-box {
+        display: none;
+    }
+}
+
+.navbar .nav > .nav-user > section a {
+    color: #fff;
+    text-decoration: none;
+    display: block;
+}
+
+.navbar .nav > .nav-user > section:hover {
+    background: rgba(255, 255, 255, 0.1);
+}
+
+.navbar .nav > .nav-user > section:hover > .nav-user-menu a:hover {
+    color: #f28d1a;
+}
+
+.nav-user section img {
+    width: 57px;
+    height: 57px;
+    margin-right: 15px;
+    margin-top: -5px;
+}
+
+.nav-user .nav-user-menu, .nav-user .signin-box {
+    z-index: 110;
+    background: #414145;
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.3);
+    top: 57px;
+    right: 0;
+    position: absolute;
+}
+.nav-user .nav-user-menu {
+    width: 220px;
+}
+
+.nav-user .nav-user-menu ul {
+    padding: 12px 15px 12px 20px;
+}
+
+.nav-user .nav-user-menu li {
+    line-height: 31px;
+}
+
+.nav-user .nav-user-menu a {
+    color: #fff;
+}
+
+.nav-user .nav-user-menu span {
+    float: right;
+    font-size: 20px;
+}
+
+.nav-user .nav-user-menu span.normal {
+    float: none;
+}
+
+.nav-user .nav-user-menu hr {
+    margin: 7px 0 5px;
+}
+
+.nav-user .signin-box {
+    width: 300px;
+    color: #fff;
+}
+
+.nav-user .signin-box form {
+    padding: 15px 20px 12px;
+}
+
+.nav-user .signin-box .input-group-addon {
+    background: #fff;
+    border-top-left-radius: 3px;
+    border-bottom-left-radius: 3px;
+}
+
+.nav-user .signin-box .form-control {
+    border-left: 0;
+    box-shadow: none;
+    border-radius: 3px;
+}
+
+.nav-user .signin-box div {
+    font-size: 13px;
+}
+
+.nav-user .signin-box .checkbox {
+    float: left;
+    margin-top: 4px;
+}
+
+.nav-user .signin-box .signin-box-buttons {
+    float: left;
+    width: 100%;
+}
+
+.nav-user .signin-box .signin-box-register {
+    background: #f28d1a;
+    float: left;
+    font-size: 14px;
+    font-weight: 600;
+    margin: 11px 0 0;
+    padding: 7px 21px;
+    width: 100%;
+    text-align: center;
+}
+
+.nav-user .signin-box label {
+    vertical-align: middle;
+}
+
+.nav-user .btn-success {
+    width: 42%;
+}
+
+.nav-user .btn-github {
+    font-weight: 600;
+    padding: 6px 12px 5px;
+    width: 56%;
+}
+
+.nav-user .btn-github span {
+    font-size: 20px;
+    margin: 0 4px 0 0;
+    vertical-align: bottom;
+    position: relative;
+    top: 2px;
+}
+
+
+/* overrides for dropdown menu on mobile */
+@media (max-width: 767px) {
+    .navbar .nav > li {
+        height: 57px;
+    }
+    .navbar-collapse.in {
+        overflow-y: visible;
+    }
+    .nav-user .nav-user-menu {
+        right: 0;
+    }
+    .nav-user-signin {
+        width: 100%;
+    }
+    .nav-user .signin-box {
+        left: auto;
+        right: 0;
+    }
+}
+
+
+
+.wrap {
+    min-height: 100%;
+    height: auto;
+    margin: 0 auto -135px;
+    padding: 0 0 135px;
+    position: relative;
+}
+
+.wrapper .container {
+    padding: 26px 20px;
+    color: #2d2d32;
+}
+.wrapper .container.flash-container {
+  padding-bottom: 0;
+}
+
+.content p {
+    font-size: 14px;
+    margin-top: 10px;
+    line-height: 22px;
+}
+
+.wrapper-search .container {
+    padding: 10px 20px 15px;
+}
+@media (min-width: 768px) {
+  .wrapper-search .container.with-description {
+    padding-left: 143px;
+  }
+}
+.wrapper-search .container p {
+  margin-top: 8px;
+  margin-bottom: 0;
+}
+
+.wrapper .container .btn-white {
+    margin: -3px 0 0 5px;
+}
+
+.wrapper-search {
+    background: #f28d1a;
+}
+
+.wrapper-search .container {
+    color: #fff;
+}
+
+.wrapper-search a {
+    color: #fff;
+    border-bottom: 1px dotted rgba(255, 255, 255, 0.6);
+}
+
+.wrapper-search a:hover {
+    text-decoration: none;
+}
+
+.wrapper-footer {
+    background: #2d2d32;
+    font-size: 14px;
+    line-height: 16px;
+    padding: 35px 0 20px;
+}
+footer p {
+  float: left;
+  color: #c3c3c3;
+}
+
+.wrapper-footer ul {
+    list-style: none;
+}
+
+.wrapper-footer li {
+    padding-bottom: 8px;
+}
+
+.wrapper-footer a {
+    color: #fff;
+}
+
+.wrapper-footer .social {
+    font-size: 26px;
+}
+
+.wrapper-footer .social li {
+    float: right;
+    margin-left: 15px;
+}
+
+
+#search-form {
+    margin-top: 5px;
+}
+
+#search-form input[type="search"] {
+    background: #FFF;
+    font: 18px 'Open Sans', sans-serif;
+    border: 0;
+    padding: 0px 10px;
+    color: #2d2d32;
+    outline: none;
+}
+
+
+button {
+    font-family: 'Open Sans', sans-serif;
+}
+
+.form-control {
+    min-height: 20px;
+    padding: 7px 11px 6px 9px;
+    box-shadow: none;
+    border-radius: 2px;
+    font-family: 'Open Sans', sans-serif;
+
+    transition: none;
+    -o-transition: none;
+    -moz-transition: none;
+    -webkit-transition: none;
+}
+
+.form-control[disabled] {
+    background: #fff;
+}
+
+.form-control:focus {
+    padding: 6px 9px 5px;
+    box-shadow: none;
+    border-width: 2px;
+    border-radius: 0 2px 2px 0;
+}
+
+.form-control[readonly] {
+    cursor: pointer;
+}
+
+.form-control[readonly]:focus {
+    padding: 7px 11px 6px 9px;
+    border-radius: 2px;
+    box-shadow: none;
+}
+
+.form-control[data-api-token] {
+    font-size: 16px;
+    height: 42px;
+    padding: 5px 11px 7px 9px;
+}
+
+input:focus:invalid:focus, textarea:focus:invalid:focus, select:focus:invalid:focus {
+    box-shadow: none;
+}
+
+.input-group {
+    display: inline;
+}
+
+.input-group .input-group-addon {
+    float: left;
+    min-height: 22px;
+    width: 10%;
+    line-height: 20px;
+    padding: 6px 0;
+    margin: 0 -4px 6px 0;
+    border-right: 0;
+    background: none;
+    color: #c6cacd;
+    font-size: 16px;
+}
+
+.input-group .form-control {
+    width: 90%;
+    float: right;
+    margin: 0 0 6px;
+}
+
+.api-token-group {
+    display: block;
+    min-height: 45px;
+}
+.api-token-group #api-token {
+  width: 85%;
+  position: absolute;
+  right: 0;
+  text-align: center;
+}
+.api-token-group .btn-show-api-token {
+  width: 145px;
+  position: absolute;
+  z-index: 10;
+}
+
+.input-group input.input-lg ~ .input-group-addon {
+    line-height: 32px;
+    font-size: 20px;
+}
+
+.input-group .input-group-addon-button {
+    padding: 0;
+}
+
+.input-group .input-group-addon .btn {
+    padding: 6px 20px;
+    border-width: 0 0 2px;
+}
+
+.input-group input:focus ~ .input-group-addon {
+    padding-top: 5px;
+    padding-bottom: 5px;
+    border: 2px solid rgba(82, 168, 236, 0.8);
+    border-right: 0;
+    border-radius: 2px 0 0 2px;
+}
+
+.input-group input:focus ~ .input-group-addon span {
+    margin-left: -1px;
+    color: rgb(82, 168, 236);
+}
+
+ul.packages {
+    margin-top: 20px;
+}
+
+ul.packages h1 {
+  font-family: Verdana;
+  font-size: 22px;
+  line-height: 1em;
+  font-weight: normal;
+  margin: 0;
+  padding: 8px 4px 0 0;
+  height: 32px;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
+}
+
+ul.packages .metadata {
+  color: #555;
+  font-size: 15px;
+}
+ul.packages .metadata .glyphicon {
+  top: 3px;
+  margin: 0 1px;
+  font-size: 18px;
+}
+ul.packages .metadata-block {
+  display: block;
+  margin-bottom: 10px;
+}
+ul.packages .metadata-block:last-child {
+  margin-bottom: 0;
+}
+
+.input-group input:focus:invalid:focus {
+    border-color: #fa3a15;
+}
+
+.input-group input:focus:invalid:focus ~ .input-group-addon {
+    border-color: #fa3a15;
+}
+
+.input-group input:focus:invalid:focus ~ .input-group-addon span {
+    color: #fa3a15;
+}
+
+.input-group input, .input-group select {
+    border-left: 0;
+}
+
+.input-group-addon:last-child, .input-group .btn:last-child {
+    border-radius: 2px 0 0 2px;
+}
+
+.input-group .form-control:first-child, .input-group-addon:first-child {
+    border-bottom-left-radius: 0;
+    border-top-left-radius: 0;
+}
+
+.input-group-addon:last-child {
+    border-left: 1px solid;
+    border-right: 0;
+}
+
+.nav-tabs {
+    border: 0;
+}
+
+.nav-tabs li a {
+    padding: 11px 20px;
+    background: #fff;
+    color: #515966;
+    font-weight: 600;
+    border-radius: 0;
+}
+
+.nav-tabs li:first-child a {
+    border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs li:last-child a {
+    border-radius: 0 0 4px 4px;
+}
+
+.nav-tabs li.active a, .nav-tabs.nav-stacked > li > a:hover {
+    background: #f28d1a;
+    color: #fff;
+}
+
+.nav-tabs.nav-stacked > li > a, .nav-tabs.nav-stacked > li > a:hover {
+    border: 0;
+}
+
+.nav-tabs.nav-stacked > li > a:hover {
+    background: #f28d1a;
+}
+
+.nav-tabs.nav-stacked [class^="icon-"], .nav-tabs.nav-stacked [class*=" icon-"] {
+    float: right;
+    font-size: 20px;
+    margin-top: -2px;
+}
+
+.nav-stacked>li+li {
+  margin-top: 0;
+}
+.nav-tabs>li{
+  margin-bottom: 0;
+}
+
+.btn {
+    padding: 9px 20px;
+    box-shadow: none;
+    border: 0;
+    border-bottom: 4px solid;
+    border-radius: 3px;
+    text-shadow: none;
+    font-weight: 600;
+}
+.btn-group-xs .btn {
+    padding: 5px 10px;
+    font-size: 12px;
+}
+
+.btn.loading, .btn.loading:hover, .btn.loading:active {
+    background-image: url("../img/loader.gif");
+    background-position: 95% 50%;
+    background-repeat: no-repeat;
+}
+
+.btn-default {
+    background: #bbbfc4;
+    border-bottom-color: #a5aab0;
+    color: #fff;
+    font-size: 14px;
+}
+.btn-default:focus, .btn-default:active {
+  color: #fff;
+  background: #a5aab0;
+  border-bottom-color: #bbbfc4;
+}
+
+.btn.disabled {
+    border-bottom-color: #e1e1e8;
+}
+
+.btn:hover {
+    background: #bbbfc4;
+    color: #fff;
+}
+
+.btn.disabled:hover {
+    background: #e6e6e6;
+    color: #333;
+}
+
+.btn-danger, .btn-danger:hover {
+    background: #ff4533;
+    border-bottom-color: #cd3729;
+}
+.btn-danger:focus, .btn-danger:active {
+    background: #cd3729;
+    border-bottom-color: #ff4533;
+}
+
+.btn-inverse, .btn-inverse:hover {
+    color: #fff;
+    background: #454e5c;
+    border-bottom-color: #303339;
+}
+.btn-inverse:focus, .btn-inverse:active {
+    color: #fff;
+    background: #303339;
+    border-bottom-color: #454e5c;
+}
+
+.btn-primary, .btn-primary:hover {
+  background: #3594e2;
+  border-bottom-color: #2f6cca;
+}
+.btn-primary:focus, .btn-primary:active {
+  background: #2f6cca;
+  border-bottom-color: #3594e2;
+}
+
+.btn-success, .btn-success:hover {
+    background: #83c129;
+    border-bottom-color: #69ad21;
+}
+.btn-success:focus, .btn-success:active {
+    background: #69ad21;
+    border-bottom-color: #83c129;
+}
+
+.btn-warning, .btn-warning:hover {
+    background: #ed9e2e;
+    border-bottom-color: #F28D1A;
+}
+.btn-warning:focus, .btn-warning:active {
+    background: #F28D1A;
+    border-bottom-color: #ed9e2e;
+}
+
+.btn-github {
+    line-height: 26px;
+    font-weight: 600;
+    color: #fff;
+    text-align: center;
+}
+
+.btn-github span {
+    margin: 0 15px 0 0;
+    vertical-align: text-top;
+    font-size: 22px;
+}
+
+/* Search */
+#search_query_query {
+  display: inline;
+}
+
+.wrapper-search {
+  clear: both;
+}
+.wrapper-search .sortable a {
+  border: 0;
+}
+
+#search_query_orderBys {
+  display: none;
+}
+
+.sortable .glyphicon.active {
+  color: #2d2d32;
+}
+
+.sortable #order-bys-wrapper {
+  font-size: 24px;
+  position: relative;
+  top: 2px;
+  margin-left: -12px;
+  padding-right: 0;
+  white-space: nowrap;
+  overflow: hidden;
+  height: 30px;
+}
+
+.sortable .order-by-group .icon:first-child {
+  margin-right: 5px;
+}
+
+.sortable #order-bys-wrapper a {
+  margin-right: 7px;
+  display: inline-block;
+}
+
+.sortable #order-bys-wrapper a.clear {
+  width: auto;
+  margin-right: 0px;
+  margin-left: 5px;
+}
+
+#search-form .submit-wrapper {
+  width: 100px;
+  float: right;
+  display: none;
+}
+.no-js #search-form .submit-wrapper {
+  display: block;
+}
+#search-form .submit {
+  margin: 0;
+  padding: 6px 20px;
+  width: 100px;
+}
+#search-form p {
+  margin: 0;
+}
+.search-list {
+  margin-top: 10px;
+}
+
+
+.package h2.title {
+    margin-bottom: -1px !important;
+    position: relative;
+}
+.package .action input.loading, .package .action input.loading:hover, .package .action input.loading:active {
+  background-position: 10px center;
+  background-image: url("../img/loader.gif");
+  background-repeat: no-repeat;
+  padding-left: 35px;
+}
+.package .action {
+  margin-right: 5px;
+  margin-bottom: 15px;
+  display: inline-block;
+}
+.package, .package p {
+    font-size: 14px;
+}
+
+.package p.description {
+    margin-top: 0;
+    font-size: 25px;
+    font-style: italic;
+    font-weight: 300;
+    margin-bottom: 15px;
+}
+@media (max-width: 767px) {
+    .package p.description {
+        font-size: 20px;
+    }
+}
+.package .details {
+    line-height: 200%;
+}
+.package .details p > i:first-child {
+    margin-right: 5px;
+}
+.package .details p, .package .facts p {
+    margin: 0;
+}
+.package .details .maintainers {
+  padding-bottom: 6px;
+  font-size: 0.0001px;
+}
+.package .details .maintainers img {
+  margin-bottom: 4px;
+  margin-right: 4px;
+}
+.package .details #add-maintainer {
+  margin-top: 3px;
+  margin-bottom: 7px;
+  float: right;
+  font-size: 34px;
+}
+.package .details #remove-maintainer {
+  float: right;
+  font-size: 35px;
+  margin-top: 5px;
+}
+.package .details .canonical {
+  text-overflow: ellipsis;
+  overflow: hidden;
+  width: 100%;
+  white-space: nowrap;
+}
+
+.package .facts {
+    margin-top: 10px;
+}
+@media (max-width: 991px) and (min-width: 768px) {
+    .package .facts {
+        margin-top: 0;
+    }
+}
+.package .facts span {
+    display: inline-block;
+    width: 100px;
+}
+
+.package .tags a:before {
+  content: "#";
+}
+.package .tags a {
+  margin-right: 10px;
+  display: inline-block;
+}
+
+.btn-github.disabled {
+    background: #ecf0f1;
+    color: #bdc3c7;
+}
+
+
+
+.package .readme {
+  padding-bottom: 20px;
+}
+.wrapper .content .package .readme img {
+  max-width: 100%;
+}
+.wrapper .content .package .readme h1:first-child,
+.wrapper .content .package .readme p:first-child,
+.wrapper .content .package .readme h2:first-child,
+.wrapper .content .package .readme h3:first-child {
+  margin-top: 0;
+}
+.wrapper .content .package .readme h1 {
+  font-size: 20px;
+  font-weight: 300;
+  padding: 0;
+  margin: 25px 0 10px;
+  line-height: 100%;
+}
+.wrapper .content .package .readme h2 {
+  font-size: 16px;
+  font-weight: 600;
+  padding: 0;
+  margin: 25px 0 10px;
+  line-height: 100%;
+}
+.wrapper .content .package .readme h3,
+.wrapper .content .package .readme h4,
+.wrapper .content .package .readme h5,
+.wrapper .content .package .readme h6 {
+  font-size: 14px;
+  font-weight: 600;
+  padding: 0;
+  margin: 15px 0 5px;
+  line-height: 100%;
+}
+
+.wrapper .content .package .readme table {
+  margin: 0 0 15px;
+}
+.wrapper .content .package .readme th,
+.wrapper .content .package .readme td {
+  border: 1px solid #CCC;
+  padding: 5px 10px;
+}
+.wrapper .content .package .readme th {
+  background: #EEE;
+}
+.wrapper .content .package .readme table code {
+  font-size: 13px;
+}
+
+.package .package-aside {
+  border-top: 1px solid #f28d1a;
+  border-radius: 0;
+
+  margin: 0;
+  padding: 20px 0 0;
+}
+.package .package-aside.versions-wrapper {
+  margin-left: 15px;
+  margin-right: 15px;
+}
+.package-aside .details {
+  padding-left: 0;
+}
+.package .package-aside i {
+  color: #bbbfc4;
+}
+.package .package-aside i:hover {
+  color: #a5aab0;
+}
+
+.package .details-toggler.open, .package .details-toggler.open a, .package .details-toggler.open i {
+  background: #f28d1a;
+  color: #fff;
+}
+
+.package .package-aside.versions-wrapper, .package .version-details {
+  margin-top: 20px;
+}
+.package .version-details .title {
+  border-top: 1px solid #f28d1a;
+  padding-top: 10px;
+}
+.package .versions {
+  list-style: none;
+  line-height: 170%;
+  overflow-y: hidden;
+  max-height: 300px;
+  padding: 0;
+  margin: 0;
+}
+.package .versions-expander {
+  text-align: center;
+  cursor: pointer;
+  margin: 10px 0 0;
+  line-height: 25px;
+}
+.package .versions .version {
+  padding: 2px 0 2px 5px;
+}
+.package .versions .version-number {
+  display: block;
+  float: left;
+  width: 91%;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
+.package .details-toggler {
+  cursor: pointer;
+}
+
+.package .versions .version {
+  position: relative;
+  display: block;
+  height: 26px;
+}
+.package .delete-version {
+  display: inline-block;
+}
+.package .delete-version .submit {
+  position: absolute;
+  right: 5px;
+  top: 7px;
+}
+.package .delete-version .submit:hover {
+  color: #cd3729;
+}
+
+@media (min-width: 992px) {
+    .package-aside .details {
+      padding-left: 15px;
+    }
+    .package .package-aside {
+      background: #fff;
+      border-radius: 2px;
+      border-top: 0;
+      padding: 10px 0;
+      margin-left: -15px;
+      margin-right: -15px;
+    }
+    .package .package-aside.versions-wrapper {
+      margin-left: 0;
+      margin-right: 0;
+      padding: 15px;
+    }
+    .package .versions {
+      margin: 0 -15px;
+    }
+    .package .versions .version {
+      padding: 2px 15px 2px 15px;
+    }
+    .package .delete-version .submit {
+      right: 15px;
+    }
+}
+@media (max-width: 767px) {
+  .package-aside .facts {
+    padding-left: 0;
+  }
+  .wrapper-footer {
+    font-size: 12px;
+  }
+  .wrapper-footer a {
+    white-space: nowrap;
+  }
+}
+
+.package .version-details .version-number {
+  font-size: 16px;
+  font-weight: 600;
+  color: #2d2d32;
+}
+.version-details.loading .title .version-number:after {
+  display: inline-block;
+  content: "";
+  width: 16px;
+  height: 16px;
+  background-image: url("../img/loader-white.gif");
+  background-repeat: no-repeat;
+  position: relative;
+  top: 2px;
+  left: 10px;
+}
+.package .version-details .release-date {
+  float: right;
+  position: relative;
+  top: 3px;
+  font-size: 12px;
+}
+.package .version-details .link-type {
+    font-weight: 600;
+    text-transform: lowercase;
+}
+.package i {
+    color: #2d2d32;
+}
+.package .version-details .source-reference {
+    margin-left: 10px;
+    display: inline-block;
+}
+.package .version-details .license {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.package .version-details .license .unknown, .abandoned i {
+  color: #c00;
+}
+.package .version-details .authors {
+  list-style: none;
+  display: inline;
+  margin: 0;
+  padding: 0;
+}
+.package .version-details .authors li {
+  display: inline;
+  margin-right: 5px;
+}
+.package .version-details .authors li:after {
+  content: ", ";
+}
+.package .version-details .authors li:last-child:after {
+  content: "";
+}
+.package .version-details .package-links {
+  border-top: 1px solid #f28d1a;
+  border-bottom: 1px solid #f28d1a;
+  margin-top: 10px;
+  padding-top: 10px;
+  padding-bottom: 10px;
+  margin-bottom: 20px;
+}
+.package .version-details .package-links ul  {
+    padding-left: 18px;
+}
+.package .version-details .package-links .no-links {
+    color: #999;
+}
+
+.package .requireme {
+    margin-top: 15px;
+}
+.package .requireme input {
+  cursor: text;
+  border: 0 !important;
+  border-radius: 0;
+  background-color: transparent;
+  font-family: Consolas, "Courier New", Courier, monospace;
+  min-width: 80%;
+  position: relative;
+  top: -3px;
+  left: 2px;
+}
+
+
+.package .mark-favorite {
+  font-size: 20px;
+  cursor: pointer;
+  color: #BDC3C7;
+  position: absolute;
+  left: -25px;
+  top: 15px;
+}
+.package .mark-favorite.is-starred {
+  color: #F28D1A;
+}
+@media (max-width: 767px) {
+    .package .mark-favorite {
+      left: auto;
+      right: 0;
+    }
+}
+
+.pagination {
+    margin: 6px 0;
+}
+
+.pagination ul {
+    list-style: none;
+    margin: 0;
+    padding-left: 0;
+}
+
+.pagination ul > li {
+    float: left;
+    line-height: 40px;
+    padding: 0;
+}
+
+.pagination ul > li > a, .pagination ul > li > span {
+    font-size: 14px;
+    font-weight: 600;
+    height: 40px;
+    text-align: center;
+    text-decoration: none;
+    padding: 0 10px;
+    display: inline-block;
+}
+.pagination ul > li > a:hover {
+    background: #F2F2F2;
+}
+
+.legend {
+  font-size: .8em;
+  margin-bottom: 10px;
+  text-align: center;
+}
+.legend li {
+  display: inline;
+  padding: 0 10px;
+}
+.legend span {
+  font-size: 1.5em;
+}
+.legend-first {
+  color: #f28d1a;
+}
+.legend-second {
+  color: #2d2d32;
+}
+
+.wrapper .content h2.title, .wrapper .content h3.title {
+    margin: -5px 0 15px;
+    padding: 0 0 15px;
+    border-bottom: 1px solid #f28d1a;
+    color: #2d2d32;
+}
+
+.wrapper .content h3.title {
+    padding: 0 0 15px;
+    font-size: 40px;
+    line-height: 45px;
+}
+.content h3.profile-title {
+    margin-top: 0;
+    margin-bottom: 20px;
+}
+
+.wrapper .content h2 {
+    font-size: 36px;
+    line-height: 44px;
+    padding-bottom: 18px;
+    padding-right: 20px;
+}
+
+.wrapper .content h2 small {
+    margin-left: 10px;
+    vertical-align: middle;
+}
+
+.wrapper .content h2 a {
+    color: #2d2d32;
+    font-weight: 600;
+    text-decoration: none;
+}
+
+@media (max-width: 767px) {
+    .wrapper .content h2.title {
+        font-size: 26px;
+        padding-bottom: 5px;
+    }
+}
+
+.wrapper .content p {
+    line-height: 150%;
+    vertical-align: baseline;
+}
+
+
+.font-slim {
+    font-weight: 300;
+}
+
+.font-normal {
+    font-weight: 400;
+}
+
+.font-bold {
+    font-weight: 500;
+}
+
+.font-fat {
+    font-weight: 600;
+}
+
+.text-center {
+    text-align: center;
+}
+
+
+.col-lg-6 .input-group input {
+    width: 89%;
+}
+
+.col-lg-6 .input-group .input-group-addon {
+    margin-right: -5px;
+    width: 8%;
+}
+
+
+#submit-package-form .form-control {
+    border-radius: 4px;
+}
+
+
+.fos_user_profile_edit .notifications {
+    margin: 10px 0;
+    float: left;
+}
+
+
+.packages-short ul {
+    list-style: none;
+    margin: 0;
+}
+
+.packages-short li {
+    margin-bottom: 12px;
+    font-style: italic;
+    font-weight: 400;
+}
+
+.packages-short li a {
+    display: block;
+    font-style: normal;
+}
+
+.packages-short li a strong {
+    margin-left: 5px;
+    font-size: 14px;
+}
+
+.packages .package-item {
+    padding: 5px 15px;
+    margin-bottom: 10px;
+    background: #fff;
+    border-radius: 4px;
+}
+
+.packages .selected .package-item {
+    padding: 4px 14px;
+    border: 1px solid #F28D1A;
+}
+
+.packages .package-item h4 {
+    font-size: 20px;
+    margin-bottom: 0;
+}
+
+.packages .package-item h4 small {
+    margin-left: 5px;
+    color: #83C129;
+    font-size: 16px;
+}
+
+.alert {
+    background: none;
+    border: none;
+    border-left: 4px solid #000;
+    box-shadow: none;
+    border-radius: 0;
+    padding: 5px 0 5px 10px;
+}
+.alert p {
+  margin: 0;
+}
+.flash-container .alert {
+  margin-bottom: 0;
+}
+.alert-warning {
+    border-left-color: #F28D1A;
+    color: #2d2d32;
+}
+.alert-danger, .alert-error {
+    border-left-color: #cd3729;
+    color: #2d2d32;
+}
+.alert-success {
+    border-left-color: #69AD21;
+    color: #2d2d32;
+}
+
+
+.chart {
+    min-height: 300px;
+}
+.version-stats-chart .chart {
+    min-height: 400px;
+}
+svg {
+    display: auto !important;
+    width: auto !important;
+    height: auto !important;
+}
+svg.chart {
+    display: block !important;
+    width:100% !important;
+    height:100% !important;
+    padding-left: 0;
+}
+@media (max-width: 767px) {
+  svg.chart {
+    margin-left: -10px;
+  }
+}
+.nvd3 text {
+  font: normal 12px 'Open Sans';
+}
+.nv-x text {
+  baseline-shift: -10px;
+}
+.nv-context .nv-x text {
+  baseline-shift: -5px;
+}
+.nvd3 .nv-axis .domain {
+  display: none;
+}
+.nvd3 .nv-axis .nv-axisMaxMin text {
+  font-weight: normal !important;
+}
+.nvd3 .nv-y .nv-axis .nv-axisMaxMin:last-child {
+  display: none;
+}
+.nvd3 .nv-x .nv-axis .nv-axisMaxMin:last-child text {
+  text-anchor: end !important;
+}
+.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
+  fill-opacity: 0 !important;
+  stroke-opacity: 0 !important;
+}
+
+@media (max-width: 1199px) {
+    .publishing-packages {
+        margin-top: 40px;
+    }
+}
+#how-to-submit-packages, #how-to-update-packages {
+    margin-top: 40px;
+}
+
+.package-installs dt {
+  float: left;
+  min-width: 125px;
+  text-align: right;
+  margin-right: 20px;
+}
+.package-installs dl {
+  margin-bottom: 5px;
+}
+.version-stats.package .package-aside.versions-wrapper {
+  margin-top: 0;
+}
+.version-stats-chart {
+  width: 70%;
+  margin-right: 5%;
+  position: relative;
+}
+
+
+[class^="icon-"]:before,
+[class*=" icon-"]:before {
+    font-family: 'fontello';
+    font-style: normal;
+    font-weight: normal;
+    font-size: 110%;
+
+    display: inline-block;
+    width: 1em;
+
+    line-height: 1;
+    text-decoration: inherit;
+    text-align: center;
+    speak: none;
+}
+
+.icon-tools:before { content: '\2692'; } /* '⚒' */
+.icon-cogs:before { content: '\26ef'; } /* '⛯' */
+.icon-mail:before { content: '\2709'; } /* '✉' */
+.icon-user-add:before { content: '\e700'; } /* '' */
+.icon-vcard:before { content: '\e722'; } /* '' */
+.icon-archive:before { content: '\e738'; } /* '' */
+.icon-share:before { content: '\e73c'; } /* '' */
+.icon-back-in-time:before { content: '\e771'; } /* '' */
+.icon-off:before { content: '\e78e'; } /* '' */
+.icon-flow-tree:before { content: '\e792'; } /* '' */
+.icon-paper-plane:before { content: '\e79b'; } /* '' */
+.icon-traffic-cone:before { content: '\e7a3'; } /* '' */
+.icon-github:before { content: '\f09b'; } /* '' */
+.icon-lightbulb:before { content: '\f0eb'; } /* '' */
+.icon-twitter:before { content: '\f309'; } /* '' */
+.icon-box:before { content: '\f330'; } /* '' */
+.icon-leaf:before { content: '🍂'; } /* '\1f342' */
+.icon-user:before { content: '👤'; } /* '\1f464' */
+.icon-users:before { content: '👥'; } /* '\1f465' */
+.icon-folder:before { content: '📁'; } /* '\1f4c1' */
+.icon-chart:before { content: '📈'; } /* '\1f4c8' */
+.icon-megaphone:before { content: '📣'; } /* '\1f4e3' */
+.icon-newspaper:before { content: '📰'; } /* '\1f4f0' */
+.icon-key:before { content: '🔑'; } /* '\1f511' */
+.icon-lock:before { content: '🔒'; } /* '\1f512' */
+.icon-lock-open:before { content: '🔓'; } /* '\1f513' */
+.icon-rocket:before { content: '🚀'; } /* '\1f680' */
diff --git a/src/Packagist/WebBundle/Resources/public/font/fontello.eot b/src/Packagist/WebBundle/Resources/public/font/fontello.eot
new file mode 100644
index 0000000..56122d5
Binary files /dev/null and b/src/Packagist/WebBundle/Resources/public/font/fontello.eot differ
diff --git a/src/Packagist/WebBundle/Resources/public/font/fontello.svg b/src/Packagist/WebBundle/Resources/public/font/fontello.svg
new file mode 100644
index 0000000..09bb3c7
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/public/font/fontello.svg
@@ -0,0 +1,138 @@
+<?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">
+<metadata>
+Created by FontForge 20100429 at Mon Feb 25 20:22:10 2013
+ By root
+Copyright (C) 2012 by original authors @ fontello.com
+</metadata>
+<defs>
+<font id="fontello" horiz-adv-x="1030" >
+  <font-face 
+    font-family="fontello"
+    font-weight="500"
+    font-stretch="normal"
+    units-per-em="1000"
+    panose-1="2 0 6 3 0 0 0 0 0 0"
+    ascent="850"
+    descent="-150"
+    bbox="14.5185 -142 1086 850"
+    underline-thickness="50"
+    underline-position="-100"
+    unicode-range="U+2692-1F680"
+  />
+<missing-glyph horiz-adv-x="364" 
+d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
+    <glyph glyph-name=".notdef" horiz-adv-x="364" 
+d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
+    <glyph glyph-name=".null" horiz-adv-x="0" 
+ />
+    <glyph glyph-name="nonmarkingreturn" horiz-adv-x="333" 
+ />
+    <glyph glyph-name="uni2692" unicode="&#x2692;" 
+d="M170 506q-14 -14 -14 -47q0 -11 -2 -11q-1 -1 -18 -16t-18 -16q-16 -13 -28 4l-70 76q-3 4 -4 8t-0.5 7t4 6.5t6 5.5t7.5 5.5t7 5.5q15 11 20 16q6 6 27 6q22 0 36.5 14.5t18.5 37.5q5 25 10 30q2 0 9 7q18 18 67 53q134 90 186 96q114 0 148 -2q13 0 -8 -8
+q-123 -54 -152 -76q-80 -56 -36 -114q35 -47 38 -48q8 -8 -2 -14q-8 -8 -38 -35t-38 -35q-14 -8 -18 -4q-42 48 -71 60t-67 -12zM866 4q17 -22 -2 -38l-48 -42q-22 -13 -38 4l-414 472q-8 8 0 20l72 62q13 8 20 -2zM994 516q-49 -88 -154 -62q-56 12 -100 -32l-82 -78
+l-68 78l68 70q40 40 37 118q-1 34 5 58q12 56 140 112q12 6 18 -3t2 -15q-10 -10 -46 -80q-13 -10 -12 -35q1 -24 40 -53q58 -40 96 22q2 4 24.5 38t23.5 36q4 10 13 9q10 -2 11 -17q16 -103 -16 -166zM152 -2l254 248l76 -86l-246 -242q-20 -20 -38 -4l-46 46q-23 18 0 38z
+" />
+    <glyph glyph-name="uni26EF" unicode="&#x26ef;" horiz-adv-x="1101" 
+d="M15 402q0 15 13 17l86 14q6 19 18 42q-23 32 -50 64q-4 5 -4 11q0 7 4 11q11 15 45 49t45 34q7 0 12 -4l64 -50q20 11 43 18q5 53 13 86q4 13 16 13h104q15 0 17 -14l13 -85q22 -7 41 -17l66 49q4 4 11 4t12 -4q81 -73 81 -90q0 -5 -4 -10l-23 -30q-21 -27 -26 -34
+q13 -25 19 -46l85 -12q13 -2 13 -17v-103q0 -15 -13 -17l-86 -14q-6 -19 -18 -42q23 -32 50 -64q4 -5 4 -11q0 -7 -4 -11q-14 -17 -46.5 -50t-43.5 -33q-6 0 -11 4l-64 50q-24 -12 -43 -17q-6 -55 -13 -87q-4 -13 -17 -13h-104q-14 0 -16 14l-13 85q-24 7 -42 17l-66 -49
+q-4 -4 -11 -4t-12 4q-80 73 -80 90q0 5 4 10l24 32l25 32q-14 25 -20 46l-85 13q-13 2 -13 16v103zM229 350q0 -58 42.5 -100.5t100.5 -42.5t100.5 42.5t42.5 100.5t-42.5 100.5t-100.5 42.5t-100.5 -42.5t-42.5 -100.5zM855 -77q-5 -9 -27 -37t-27 -28l-7 4l-26 15l-36 21
+q-3 3 -3 4q0 13 29 77q-11 14 -17 29q-83 9 -83 17v78q0 9 83 18q5 11 17 29q-29 64 -29 77q0 2 1 3.5t3 2.5t4.5 2t6.5 3t7 4l16 10q11 6 17 9q15 9 17 9q5 0 27 -28t27 -36q7 1 17 1t17 -1q33 45 51 63l4 1q2 0 69 -39q2 -2 2 -4q0 -15 -28 -77q9 -13 16 -29q83 -9 83 -18
+v-78q0 -8 -83 -17q-7 -17 -16 -29q28 -62 28 -77q0 -2 -2 -4l-37 -21l-25 -15l-7 -4q-5 0 -27.5 28.5t-27.5 36.5q-14 -2 -17 -2t-17 2zM855 494q-5 -8 -27 -36t-27 -28q-1 0 -69 39q-3 3 -3 4q0 13 29 77q-11 14 -17 29q-83 9 -83 18v78q0 8 83 17q5 11 17 29
+q-29 64 -29 77q0 2 1 3.5t3 2.5t4.5 2t6.5 3t7 4l16 9q11 7 17 10q15 9 17 9q5 0 27 -28t27 -36q14 2 17 2t17 -2q33 45 51 63l4 1q2 0 69 -39q2 -2 2 -4q0 -15 -28 -77q9 -13 16 -29q83 -9 83 -17v-78q0 -9 -83 -18q-7 -17 -16 -29q28 -62 28 -77q0 -2 -2 -4
+q-67 -39 -69 -39q-5 0 -27.5 28t-27.5 36q-7 -1 -17 -1t-17 1zM801 64q0 -29 21 -50t50 -21t50.5 21t21.5 50q0 28 -22 50t-50 22t-49.5 -21.5t-21.5 -50.5zM801 636q0 -29 21 -50.5t50 -21.5t50.5 21.5t21.5 50.5q0 28 -21.5 49.5t-50.5 21.5q-28 0 -49.5 -21.5
+t-21.5 -49.5z" />
+    <glyph glyph-name="uni2709" unicode="&#x2709;" horiz-adv-x="930" 
+d="M17 626q2 14 26 14h846q38 0 20 -32q-8 -15 -24 -22l-97 -51l-180 -97q-93 -50 -97 -52q-16 -10 -46 -10q-29 0 -46 10q-4 2 -97 52l-180 97l-97 51q-32 18 -28 40zM895 486q20 11 20 -10v-368q0 -16 -17 -32t-33 -16h-800q-16 0 -33 16t-17 32v368q0 21 20 10l384 -200
+q17 -10 46 -10t46 10z" />
+    <glyph glyph-name="uniE700" unicode="&#xe700;" 
+d="M815 6v-106h-800v202q41 16 82 26q95 34 129 69q35 35 35 95q0 22 -23 48q-24 27 -31 74q-1 11 -23 25q-21 13 -25 61q0 32 14 38l4 4q-1 6 -3 19q-13 53 -8 95.5t39 85.5q46 58 160 58t160 -58t42 -112l-14 -88q18 -8 18 -42q-5 -55 -23 -60q-20 -5 -23 -26
+q-10 -46 -33 -73q-23 -28 -23 -49q0 -60 36 -95t130 -69q180 -65 180 -122zM865 400h150v-100h-150v-150h-100v150h-150v100h150v150h100v-150z" />
+    <glyph glyph-name="uniE70A" unicode="&#xe70a;" 
+d="M26 353q74 120 203.5 201.5t285.5 81.5t285.5 -81.5t203.5 -201.5q11 -20 11 -39q0 -18 -11 -38q-74 -120 -203 -201.5t-286 -81.5q-156 0 -285.5 81.5t-203.5 201.5q-11 20 -11 38q0 19 11 39zM86 314q70 -109 180 -179.5t249 -70.5t249 70.5t180 179.5
+q-85 130 -213 197q34 -58 34 -125q0 -104 -73 -177t-177 -73t-177 73t-73 177q0 67 34 125q-128 -67 -213 -197zM345 386q0 -11 8 -19t19 -8t19 8t8 19q0 47 34.5 81.5t81.5 34.5q11 0 19 8t8 19t-8 18.5t-19 7.5q-69 0 -119.5 -50t-50.5 -119z" />
+    <glyph glyph-name="uniE70B" unicode="&#xe70b;" 
+d="M26 353q85 130 212 207q125 76 277 76q43 0 100 -10l31 54q6 9 15 9q4 0 28 -12l46 -27q9 -6 9 -15v-5l-176 -316l-176 -316l-28 -50q-6 -9 -15 -9q-7 0 -75 39q-9 6 -9 16q0 5 25 49q-162 74 -264 233q-11 19 -11 38q0 20 11 39zM368 184q-45 33 -74 85.5t-29 116.5
+q0 67 34 125q-128 -67 -213 -197q92 -142 239 -209zM345 386q0 -11 8 -19t19 -8t19 8t8 19q0 47 34.5 81.5t81.5 34.5q11 0 19 8t8 19t-8 18.5t-19 7.5q-69 0 -119.5 -50t-50.5 -119zM556 67q124 11 224 79t164 168q-63 98 -158 164l35 63q53 -36 102.5 -86t80.5 -102
+q11 -20 11 -39q0 -18 -11 -38q-42 -70 -111 -132t-144 -98q-111 -53 -234 -53zM604 152l157 281q4 -23 4 -47q0 -82 -46.5 -145t-114.5 -89z" />
+    <glyph glyph-name="uniE722" unicode="&#xe722;" 
+d="M915 750q42 0 71 -29t29 -71v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-800q-40 0 -70 30t-30 70v600q0 41 29.5 70.5t70.5 29.5h800zM915 650h-800v-600h800v600zM465 156h-250v90h250v-90zM465 306h-250v90h250v-90zM465 456h-250v90h250v-90zM811 226l4 -70h-250
+q0 70 6 70q84 22 84 66q0 16 -27 56t-27 88q0 110 90 110t90 -110q0 -48 -28 -88t-28 -56q0 -20 20.5 -35.5t43.5 -22.5z" />
+    <glyph glyph-name="uniE738" unicode="&#xe738;" horiz-adv-x="1011" 
+d="M159 600q0 18 11.5 33t26.5 17h608q20 0 36 -15q14 -14 14 -35v-50h-696v50zM259 700q0 18 11.5 33t26.5 17h410q20 0 36 -15q14 -14 14 -35h-498zM955 550q33 -30 38 -46q6 -18 0 -54l-76 -450q-3 -18 -17.5 -32.5t-30.5 -17.5h-710q-52 0 -60 50q0 1 -39 225t-39 225
+q-10 22 -3 44t10 26q1 2 21 20l40 40v-80h836v80zM707 280v100h-70v-80h-260v80h-68v-100q0 -50 48 -50h300q18 0 33 10.5t15 25.5z" />
+    <glyph glyph-name="uniE73C" unicode="&#xe73c;" horiz-adv-x="830" 
+d="M665 200q62 0 106 -43.5t44 -106.5q0 -62 -44.5 -106t-105.5 -44t-105.5 44t-44.5 106q0 3 1 13t1 13l-260 156q-43 -32 -92 -32q-61 0 -105.5 44t-44.5 106t44.5 106t105.5 44q55 0 92 -30l260 156q0 2 -1 12t-1 12q0 62 44.5 106t105.5 44q62 0 106 -43.5t44 -106.5
+q0 -62 -44.5 -106t-105.5 -44q-52 0 -90 32l-262 -156q2 -10 2 -26q0 -14 -2 -24l262 -156q36 30 90 30z" />
+    <glyph glyph-name="uniE771" unicode="&#xe771;" horiz-adv-x="970" 
+d="M836 640q119 -121 119 -290t-119 -290q-118 -120 -289 -120q-139 0 -252 88l70 76q83 -60 182 -60q126 0 216 90t90 216q0 128 -90 218t-216 90q-124 0 -213 -86q-88 -86 -93 -210h142l-184 -206l-184 206h124q5 166 123 282q119 116 285 116q171 0 289 -120zM511 570h70
+v-204l130 -130l-50 -50l-150 150v234z" />
+    <glyph glyph-name="uniE78E" unicode="&#xe78e;" horiz-adv-x="887" 
+d="M15 350q0 109 47.5 197t123.5 145q28 17 55 13t45 -27.5t14 -52.5t-28 -47q-114 -87 -114 -228q0 -118 84 -202t202 -84t202 84q83 83 83 202q0 141 -114 228q-22 17 -27 46t13 54q16 23 45.5 27.5t54.5 -13.5q76 -58 123.5 -145.5t47.5 -196.5q0 -91 -35 -169.5
+t-90 -133.5q-56 -56 -134.5 -91t-168.5 -35t-168.5 35t-134.5 91t-91 134.5t-35 168.5zM372 421v358q0 28 21.5 49.5t50.5 21.5q28 0 49.5 -21t21.5 -50v-358q0 -29 -21.5 -50t-49.5 -21q-29 0 -50.5 21.5t-21.5 49.5z" />
+    <glyph glyph-name="uniE792" unicode="&#xe792;" horiz-adv-x="970" 
+d="M485 820q49 0 84.5 -35t35.5 -85q0 -76 -72 -110v-140q0 -52 78 -52h100q85 0 129 -54q43 -53 43 -118v-114q72 -34 72 -112q0 -50 -35.5 -85t-84.5 -35t-84.5 35t-35.5 85q0 78 72 112v114q0 78 -76 78h-100q-43 0 -78 12v-204q72 -34 72 -112q0 -50 -35.5 -85
+t-84.5 -35t-84.5 35t-35.5 85q0 78 72 112v204q-30 -12 -76 -12h-100q-35 0 -56.5 -22t-21.5 -56v-114q72 -34 72 -112q0 -50 -35.5 -85t-84.5 -35t-84.5 35t-35.5 85q0 78 72 112v114q0 65 43 118q44 54 131 54h100q76 0 76 52v140q-72 34 -72 110q0 50 35.5 85t84.5 35z
+M205 0q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5q0 -28 20 -48t48 -20t49 20t21 48zM417 700q0 -28 20 -48t48 -20t49 20t21 48q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5zM555 0q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5
+q0 -28 20 -48t48 -20t49 20t21 48zM835 -68q28 0 49 20t21 48q0 29 -20.5 49.5t-49.5 20.5q-28 0 -48 -20.5t-20 -49.5q0 -28 20 -48t48 -20z" />
+    <glyph glyph-name="uniE79B" unicode="&#xe79b;" horiz-adv-x="951" 
+d="M909 720q13 4 21.5 -3t4.5 -19q-3 -8 -73 -313.5t-73 -312.5q-2 -13 -13 -18.5t-25 0.5l-139 75l-139 75l22 26l209 227l145 156q38 41 40 43q4 4 -1 9t-9 1l-550 -402l-302 120q-12 4 -12 12t12 12q7 4 227 82l437 154q217 76 218 76zM327 -8v204l160 -82
+q-122 -108 -142 -128q-18 -14 -18 6z" />
+    <glyph glyph-name="uniE7A3" unicode="&#xe7a3;" horiz-adv-x="991" 
+d="M495 432q58 0 108.5 16.5t61.5 47.5q2 -6 21 -58l35 -100q-5 -38 -70 -65q-66 -27 -156 -27t-156 27q-65 27 -70 65l35 100q19 52 21 58q11 -31 62.5 -47.5t107.5 -16.5zM371 624l52 142q10 34 72 34t74 -34q11 -34 50 -142q-29 -44 -124 -44t-124 44zM935 196
+q41 -16 41 -41.5t-37 -46.5l-352 -188q-37 -22 -91 -22t-91 22l-354 188q-38 20 -36 46t42 42l188 76l-22 -60q0 -47 80 -82q78 -34 192 -34t192 34q81 35 82 82l-22 60z" />
+    <glyph glyph-name="uniF09B" unicode="&#xf09b;" horiz-adv-x="887" 
+d="M15 350q0 123 60 219.5t153 151.5q101 58 216 58q123 0 219.5 -60t151.5 -154q57 -99 57 -215q0 -123 -60 -219.5t-153 -151.5q-101 -58 -215 -58q-123 0 -220 60t-152 154q-57 99 -57 215zM86 350q0 -115 70 -211q69 -95 180 -129v94q-27 -4 -38 -4q-61 0 -85 56
+q-11 29 -32 46q-23 19 -23 22q0 7 15 7q10 0 18.5 -2.5t17 -10t11.5 -10.5t12 -16l9 -12q21 -28 54 -28q21 0 45 8q9 30 35 49q-91 9 -137 47q-44 37 -44 125q0 67 40 111q-8 26 -8 47q0 30 16 60q33 0 56 -10q22 -9 56 -34q40 10 95 10q45 0 85 -9q31 22 56 33q23 10 56 10
+q15 -29 15 -60q0 -23 -8 -47q41 -47 41 -111q0 -89 -45 -126q-44 -37 -137 -46q39 -25 39 -73v-126q111 34 180 129q70 96 70 211q0 75 -29.5 141t-75.5 112t-111 75t-141 29t-141.5 -29t-111.5 -75t-75.5 -112t-29.5 -141z" />
+    <glyph glyph-name="uniF0EB" unicode="&#xf0eb;" horiz-adv-x="601" 
+d="M219 -25q-26 0 -43.5 14.5t-17.5 39.5q0 13 7 26q-25 16 -25 45q0 20 14 36q-14 16 -14 35q0 31 26 46q-4 52 -52 113q-21 27 -42 49q-57 63 -57 150q0 57 25.5 104.5t64.5 77.5q86 68 196 68q109 0 195 -68q39 -30 64.5 -77.5t25.5 -104.5q0 -87 -57 -150
+q-29 -32 -43.5 -51t-31.5 -51q-18 -34 -19 -60q26 -15 26 -46q0 -19 -14 -35q14 -16 14 -36q0 -29 -25 -45q8 -14 8 -26q0 -25 -17.5 -39.5t-43.5 -14.5q-10 -23 -32 -38.5t-50 -15.5t-50 15.5t-32 38.5zM124 428q3 -3 17 -18t17 -19q71 -85 79 -166h127q8 81 79 166
+q3 4 17 19t17 18q38 44 38 101q0 63 -39.5 105t-98.5 61q-37 12 -76 12q-80 0 -145 -47q-70 -51 -70 -131q0 -57 38 -101zM283 600q0 8 5.5 13t12.5 5q65 0 104 -39q21 -21 21 -50q0 -7 -5 -12.5t-13 -5.5t-13 5.5t-5 12.5q0 25 -30 39q-31 14 -59 14q-7 0 -12.5 5t-5.5 13z
+" />
+    <glyph glyph-name="uniF309" unicode="&#xf309;" horiz-adv-x="950" 
+d="M935 636q-37 -56 -94 -98v-24q0 -130 -60 -250q-60 -121 -186 -203q-127 -83 -290 -83q-159 0 -290 84q18 -2 46 -2q131 0 234 80q-64 2 -111.5 39t-64.5 93q9 -4 34 -4q26 0 50 6q-63 13 -106.5 64.5t-43.5 121.5v2q36 -20 84 -24q-84 58 -84 158q0 47 26 94
+q155 -189 390 -196q-6 18 -6 42q0 78 55.5 133t134.5 55q82 0 136 -58q58 12 120 44q-19 -66 -82 -104q55 8 108 30z" />
+    <glyph glyph-name="uniF330" unicode="&#xf330;" horiz-adv-x="990" 
+d="M301 806l194 -158l-284 -184l-196 164zM681 54l116 78v-46l-302 -190l-302 190v46l118 -78q6 -2 12 -2q10 0 14 4l158 132l160 -132q4 -4 14 -4q6 0 12 2zM975 628l-194 -164l-286 184l196 158zM495 286l286 178l174 -140l-282 -184zM319 140l-282 184l174 140l284 -178z
+" />
+    <glyph glyph-name="u1F342" unicode="&#x1f342;" horiz-adv-x="970" 
+d="M251 646q182 107 506 66q169 -23 196 -50q4 -5 -2 -10q-76 -40 -130 -109t-78 -132q-23 -61 -65 -132q-40 -69 -93 -105q-137 -96 -382 -4q-67 -78 -114 -176q-12 -24 -47 -7t-25 39q43 100 129 193q87 95 176 153q179 119 317 174l54 20q-65 0 -148.5 -15.5
+t-144.5 -37.5q-70 -25 -162 -84q-89 -57 -161 -141q-23 242 174 358z" />
+    <glyph glyph-name="u1F464" unicode="&#x1f464;" horiz-adv-x="970" 
+d="M751 128q204 -72 204 -122v-106h-940v106q0 50 204 122q93 34 128 69q34 34 34 95q0 23 -22 49q-21 26 -32 73q-3 21 -23 26q-18 5 -23 60q0 32 14 38l4 4q-7 46 -12 88q-4 53 41 112q45 58 157 58t158 -58t40 -112l-12 -88q18 -8 18 -42q-5 -55 -23 -60q-20 -5 -23 -26
+q-7 -47 -31 -74q-23 -26 -23 -48q0 -60 35 -95q34 -34 127 -69z" />
+    <glyph glyph-name="u1F465" unicode="&#x1f465;" 
+d="M791 -90v150q0 54 -30 81q-32 29 -154 89q40 30 40 84q0 17 -13 33t-19 51q-2 8 -14 16q-13 8 -14 42q0 24 12 30q-10 40 -8 73.5t23 64.5q27 40 95 40t96 -40q29 -40 24 -78l-8 -60q12 -6 12 -30q-1 -34 -14 -42q-12 -8 -14 -16q-6 -35 -19 -51t-13 -33q0 -42 21 -66
+t77 -48q113 -47 130 -80q6 -7 9 -61q4 -71 5 -101v-48h-224zM527 172q182 -78 182 -124v-138h-694v184q0 44 84 78q73 30 104 64q28 31 28 88q0 21 -19 44t-25 68q-1 10 -18 22q-15 11 -20 56q0 26 10 36l4 2l-5 46l-5 36q-5 50 33 103t127 53t127 -53t33 -103l-10 -82
+q14 -8 14 -38q-5 -45 -20 -56q-17 -12 -18 -22q-6 -45 -25 -68t-19 -44q0 -57 28 -88q31 -34 104 -64z" />
+    <glyph glyph-name="u1F4B3" unicode="&#x1f4b3;" 
+d="M915 700q41 0 70.5 -29.5t29.5 -70.5v-500q0 -42 -29 -71t-71 -29h-800q-41 0 -70.5 29.5t-29.5 70.5v500q0 40 30 70t70 30h800zM915 400h-800v-300h800v300zM915 600h-800v-50h800v50zM245 294v-30h-30v30h30zM425 234v30h30v30h60v-30h-30v-30h-30v-30h-60v30h30z
+M485 204v30h30v-30h-30zM305 204v30h60v-30h-60zM395 234h-30v60h60v-30h-30v-30zM275 234v-30h-60v30h30v30h30v30h60v-30h-30v-30h-30z" />
+    <glyph glyph-name="u1F4C1" unicode="&#x1f4c1;" horiz-adv-x="1031" 
+d="M969 500q29 0 38.5 -10.5t7.5 -37.5l-42 -452q-2 -27 -13.5 -38.5t-40.5 -11.5h-806q-51 0 -56 50l-42 452q-2 27 7.5 37.5t38.5 10.5h908zM935 610l10 -40h-846l14 132q4 19 20 33.5t36 14.5h164q52 0 86 -34l30 -30q31 -36 86 -36h340q20 0 38 -12t22 -28z" />
+    <glyph glyph-name="u1F4C8" unicode="&#x1f4c8;" horiz-adv-x="1033" 
+d="M49 284q-42 11 -32 56q11 42 54 32l98 -24l-52 -80zM939 272q14 12 33.5 11t30.5 -15q33 -32 -2 -64l-252 -226q-12 -12 -30 -12q-15 0 -28 10l-286 220l-54 14l50 80l36 -8q13 -5 16 -8l264 -204zM449 492l-350 -550q-12 -22 -38 -22q-25 0 -39 24t1 46l374 588
+q7 15 28 20q18 6 36 -6l246 -156l226 326q10 16 27.5 19t34.5 -9q38 -24 12 -62l-252 -362q-24 -36 -62 -12z" />
+    <glyph glyph-name="u1F4E3" unicode="&#x1f4e3;" horiz-adv-x="890" 
+d="M873 242q9 -120 -39 -140q-28 -12 -61 3q-36 16 -65 40q-37 28 -106.5 42t-141.5 7q-26 -4 -41.5 -18t-6.5 -38q26 -65 46 -108q4 -10 24 -22q19 -12 24 -20q13 -34 -22 -46q-45 -20 -102 -40q-30 -10 -54 42q-23 56 -58 132q-6 13 -34 17q-27 4 -46 31l-20 -7
+q-14 -5 -19 -6.5t-16 -3.5t-18 -0.5t-17 5t-22 10.5q-40 24 -54 60q-16 30 -5 79q11 47 43 61q126 52 213 108q84 54 124 103q34 42 59 92q22 44 25 78q7 76 51 95q48 19 130 -70t142 -228q58 -137 67 -258zM778 200q9 5 10 38q2 38 -11 98q-27 133 -108 251q-40 58 -67 84
+q-29 27 -36 23q-9 -5 -10 -42q-2 -40 10 -105q25 -141 108 -252q44 -57 68 -76q29 -23 36 -19z" />
+    <glyph glyph-name="u1F4F0" unicode="&#x1f4f0;" horiz-adv-x="830" 
+d="M715 800q42 0 71 -29t29 -71v-700q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-40 0 -70 30t-30 70v700q0 41 29.5 70.5t70.5 29.5h600zM715 700h-600v-700h600v700zM465 200h-250v50h250v-50zM615 400h-200v50h200v-50zM415 600h200v-100h-200v100zM365 400h-150v200h150v-200
+zM315 300h-100v50h100v-50zM365 350h250v-50h-250v50zM615 100h-400v50h400v-50zM515 200v50h100v-50h-100z" />
+    <glyph glyph-name="u1F511" unicode="&#x1f511;" horiz-adv-x="810" 
+d="M761 397q-48 -99 -150 -117q-67 -12 -130 -2l-118 -194l-70 -12l-104 -166q-13 -27 -46 -32l-76 -14q-13 -4 -22.5 4.5t-11.5 21.5l-16 98q-8 30 12 56l258 386q-25 51 -38 120q-10 57 7 108t52.5 87t81.5 60t97 33q107 20 195 -45q89 -65 107 -177q20 -117 -28 -215z
+M663 536q30 49 20 100t-50 80q-53 37 -109 17.5t-75 -72.5q-4 -10 4 -36q4 -13 31 -32q12 -8 67 -49q6 -5 42 -28q13 -8 19 -8q32 0 51 28z" />
+    <glyph glyph-name="u1F512" unicode="&#x1f512;" horiz-adv-x="673" 
+d="M69 -7q-23 0 -38.5 15.5t-15.5 37.5v322q0 22 15.5 37.5t38.5 15.5h17v108q0 102 74 176t176 74q103 0 177 -74q73 -73 73 -176v-108h18q23 0 38.5 -15.5t15.5 -37.5v-322q0 -22 -15.5 -37.5t-38.5 -15.5h-535zM336 671q-58 0 -100 -41.5t-42 -100.5v-108h285v108
+q0 58 -42 100t-101 42zM265 279q0 -44 39 -64l-38 -128q-3 -9 2.5 -16t14.5 -7h107q9 0 14.5 7t2.5 16l-38 128q39 20 39 64q0 29 -21.5 50t-50.5 21t-50 -21t-21 -50z" />
+    <glyph glyph-name="u1F513" unicode="&#x1f513;" horiz-adv-x="959" 
+d="M69 -7q-23 0 -38.5 15.5t-15.5 37.5v322q0 22 15.5 37.5t38.5 15.5h375v108q0 103 73 176q74 74 177 74q102 0 176 -74t74 -176v-143q0 -14 -11 -25t-25 -11h-36q-14 0 -25 11t-11 25v143q0 59 -42 100.5t-100 41.5q-59 0 -101 -42t-42 -100v-108h53q23 0 38.5 -15.5
+t15.5 -37.5v-322q0 -22 -15.5 -37.5t-38.5 -15.5h-535zM265 82q0 -8 5.5 -13t12.5 -5h107q8 0 13 5t5 13q0 4 -9 32l-19 64l-11 37q39 19 39 64q0 29 -21.5 50t-50.5 21t-50 -21t-21 -50q0 -44 39 -64l-1 -4q-1 -4 -4 -13l-6 -20q-3 -9 -20 -64q-8 -25 -8 -32z" />
+    <glyph glyph-name="u1F680" unicode="&#x1f680;" horiz-adv-x="890" 
+d="M558 236q7 -56 8 -81q2 -33 -8 -59q-4 -10 -7 -22t-4.5 -16t-6 -10t-11 -11t-19.5 -13l-33 -20q-11 -6 -16 -8.5t-27 -11.5l-39 -17q-46 -21 -85 -37q-30 -11 -44.5 3.5t-3.5 44.5l40 110l-130 132l-106 -40q-30 -13 -43.5 2.5t-2.5 45.5l16 42l25 62q12 29 17 40
+q37 75 47 81q2 1 12 8l17 12q17 13 41 13h52l71 -6q37 52 106 124q64 66 122.5 107.5t140.5 71.5q85 31 165 19q17 0 20 -20q10 -85 -18.5 -170.5t-75.5 -148.5q-99 -133 -184 -199zM608 532q22 -22 54 -22t54 23t22 55t-22 55t-54 23t-54 -23t-22 -55q0 -33 22 -56z" />
+  </font>
+</defs></svg>
diff --git a/src/Packagist/WebBundle/Resources/public/font/fontello.ttf b/src/Packagist/WebBundle/Resources/public/font/fontello.ttf
new file mode 100644
index 0000000..dc72d13
Binary files /dev/null and b/src/Packagist/WebBundle/Resources/public/font/fontello.ttf differ
diff --git a/src/Packagist/WebBundle/Resources/public/font/fontello.woff b/src/Packagist/WebBundle/Resources/public/font/fontello.woff
new file mode 100644
index 0000000..551266f
Binary files /dev/null and b/src/Packagist/WebBundle/Resources/public/font/fontello.woff differ
diff --git a/src/Packagist/WebBundle/Resources/public/img/loader-white.gif b/src/Packagist/WebBundle/Resources/public/img/loader-white.gif
new file mode 100644
index 0000000..de986a2
Binary files /dev/null and b/src/Packagist/WebBundle/Resources/public/img/loader-white.gif differ
diff --git a/src/Packagist/WebBundle/Resources/public/img/loader.gif b/src/Packagist/WebBundle/Resources/public/img/loader.gif
new file mode 100644
index 0000000..7a7cf8e
Binary files /dev/null and b/src/Packagist/WebBundle/Resources/public/img/loader.gif differ
diff --git a/src/Packagist/WebBundle/Resources/public/img/logo-small.png b/src/Packagist/WebBundle/Resources/public/img/logo-small.png
new file mode 100644
index 0000000..76c0e62
Binary files /dev/null and b/src/Packagist/WebBundle/Resources/public/img/logo-small.png differ
diff --git a/src/Packagist/WebBundle/Resources/public/img/logo.png b/src/Packagist/WebBundle/Resources/public/img/logo.png
new file mode 100644
index 0000000..289548d
Binary files /dev/null and b/src/Packagist/WebBundle/Resources/public/img/logo.png differ
diff --git a/src/Packagist/WebBundle/Resources/public/js/charts.js b/src/Packagist/WebBundle/Resources/public/js/charts.js
new file mode 100644
index 0000000..7536608
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/public/js/charts.js
@@ -0,0 +1,169 @@
+(function ($) {
+    "use strict";
+
+    var colors = [
+        '#f28d1a',
+        '#2d2d32'
+    ];
+
+    function initPackagistChart(svg, labels, series, withDatePicker) {
+        var format = d3.time.format("%Y-%m-%d");
+        if (labels[0].match(/^\d+-\d+$/)) {
+            format = d3.time.format("%Y-%m");
+        }
+
+        var chartData = [];
+        series.map(function (serie, index) {
+            var points = [];
+            labels.map(function (label, index) {
+                points.push({x: format.parse(label), y: parseInt(serie.values[index]) || 0});
+            })
+            chartData.push({
+                values: points,
+                key: serie.name,
+                color: colors[index]
+            });
+        })
+
+        if (withDatePicker && $(window).width() < 767) {
+            withDatePicker = false;
+        }
+
+        nv.addGraph(function() {
+            var chart;
+            if (withDatePicker) {
+                chart = nv.models.lineWithFocusChart();
+            } else {
+                chart = nv.models.lineChart();
+            }
+
+            function formatDate(a,b) {
+                if (!(a instanceof Date)) {
+                    a = new Date(a);
+                }
+                return format(a,b);
+            }
+            function formatDigit(a,b) {
+                if (a > 1000000) {
+                    return Math.round(a/1000000) + 'mio';
+                }
+                if (a > 1000) {
+                    return Math.round(a/1000) + 'K';
+                }
+                return a;
+            }
+
+            chart.xAxis.tickFormat(formatDate);
+            chart.yAxis.tickFormat(formatDigit);
+
+            if (withDatePicker) {
+                chart.x2Axis.tickFormat(formatDate);
+                chart.y2Axis.tickFormat(formatDigit);
+            }
+
+            d3.select(svg)
+                .datum(chartData)
+                .transition().duration(100)
+                .call(chart);
+
+            nv.utils.windowResize(chart.update);
+
+            return chart;
+        });
+    };
+
+    $('svg[data-labels]').each(function () {
+        initPackagistChart(
+            this,
+            $(this).attr('data-labels').split(','),
+            $(this).attr('data-values').split('|').map(function (values) {
+                values = values.split(':');
+                return {
+                    name: values[0],
+                    values: values[1].split(',')
+                };
+            })
+        );
+    });
+
+    window.initPackageStats = function (average, date, versions, statsUrl, versionStatsUrl) {
+        var match,
+            hash = document.location.hash,
+            versionCache = {},
+            ongoingRequest = false;
+
+        function initChart(type, res) {
+            initPackagistChart(
+                $('.js-'+type+'-dls')[0],
+                res.labels,
+                [{name: 'Daily Downloads', values: res.values}],
+                true
+            );
+        }
+
+        $.ajax({
+            url: statsUrl,
+            success: function (res) {
+                initChart('all', res);
+            }
+        })
+        function loadVersionChart(versionId) {
+            ongoingRequest = true;
+            $.ajax({
+                url: versionStatsUrl.replace('_VERSION_', versionId) + '?average=' + average + '&from=' + date,
+                success: function (res) {
+                    initChart('version', res);
+                    versionCache[versionId] = res;
+                    ongoingRequest = false;
+                }
+            });
+        }
+
+        // initializer for #<version-id> present on page load
+        if (hash.length > 1) {
+            hash = hash.substring(1);
+            match = $('.package .details-toggler[data-version-id="'+hash+'"]');
+            if (match.length) {
+                $('.package .details-toggler.open').removeClass('open');
+                match.addClass('open');
+            }
+        }
+
+        if ($('.package .details-toggler.open').length) {
+            loadVersionChart($('.package .details-toggler.open').attr('data-version-id'));
+        }
+
+        $('.package .details-toggler').on('click', function () {
+            var res, target = $(this), versionId = target.attr('data-version-id');
+
+            if (versionCache[versionId]) {
+                res = versionCache[versionId];
+                initChart('version', res);
+            } else {
+                if (ongoingRequest) {
+                    return;
+                }
+                loadVersionChart(versionId);
+            }
+
+            $('.package .details-toggler.open').removeClass('open');
+            target.addClass('open');
+        });
+
+        $(window).on('scroll', function () {
+            var basePos = $('.version-stats').offset().top;
+            var footerPadding = $(document).height() - basePos - $('footer').height() - $('.version-stats-chart').height() - 50;
+            var headerPadding = 80;
+            $('.version-stats-chart').css('top', Math.max(0, Math.min(footerPadding, window.scrollY - basePos + headerPadding)) + 'px');
+        });
+
+        // initialize version list expander
+        var versionsList = $('.package .versions')[0];
+        if (versionsList.offsetHeight < versionsList.scrollHeight) {
+            $('.package .versions-expander').removeClass('hidden').on('click', function () {
+                $(this).addClass('hidden');
+                $(versionsList).css('max-height', 'inherit');
+            });
+        }
+    };
+})(jQuery);
diff --git a/src/Packagist/WebBundle/Resources/public/js/layout.js b/src/Packagist/WebBundle/Resources/public/js/layout.js
new file mode 100644
index 0000000..d58191d
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/public/js/layout.js
@@ -0,0 +1,45 @@
+(function ($, humane) {
+    "use strict";
+
+    /**
+     * Ajax error handler
+     */
+    $.ajaxSetup({
+        error: function (xhr) {
+            var resp, message, details = '';
+
+            humane.remove();
+
+            if (xhr.responseText) {
+                try {
+                    resp = JSON.parse(xhr.responseText);
+                    if (resp.status && resp.status === 'error') {
+                        message = resp.message;
+                        details = resp.details;
+                    }
+                } catch (e) {
+                    message = "We're so sorry, something is wrong on our end.";
+                }
+            }
+
+            humane.log(details ? [message, details] : message, {timeout: 0, clickToClose: true});
+        }
+    });
+
+    /**
+     * API Token visibility toggling
+     */
+    var token = $('#api-token');
+    token.val('');
+
+    $('.btn-show-api-token,#api-token').each(function() {
+        $(this).click(function (e) {
+            token.val(token.data('api-token'));
+            token.select();
+
+            $('.btn-show-api-token').text('Your API token');
+
+            e.preventDefault();
+        });
+    });
+})(jQuery, humane);
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Resources/public/js/search.js b/src/Packagist/WebBundle/Resources/public/js/search.js
new file mode 100644
index 0000000..e6cfa8c
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/public/js/search.js
@@ -0,0 +1,163 @@
+/*jslint browser: true */
+/*global jQuery: true */
+(function ($) {
+    "use strict";
+
+    var list = $('.search-list'),
+        form = $('form#search-form'),
+        showResults,
+        doSearch,
+        searching = false,
+        searchQueued = false,
+        previousQuery = form.serialize(),
+        firstQuery = true;
+
+    showResults = function (page) {
+        var newList = $(page);
+
+        list.html(newList.html());
+        list.parent().removeClass('hidden');
+        list.find('ul.packages li:first').addClass('selected');
+        $('.order-by-group').attr('href', function (index, current) {
+            return current.replace(/q=.*?&/, 'q=' + encodeURIComponent($('input[type="search"]', form).val()) + '&')
+        });
+        $('.js-search-field-wrapper').removeClass('col-xs-12').addClass('col-xs-8');
+        $('#order-bys-wrapper').removeClass('hidden');
+
+        searching = false;
+
+        if (searchQueued) {
+            doSearch();
+            searchQueued = false;
+        }
+    };
+
+    doSearch = function () {
+        var currentQuery,
+            q,
+            pathname,
+            urlPrefix,
+            url,
+            title;
+
+        if (searching) {
+            searchQueued = true;
+            return;
+        }
+
+        if ($('#search_query_query').val().match(/^\s*$/) !== null) {
+            if (!firstQuery) {
+                list.parent().addClass('hidden');
+                $('#order-bys-wrapper').addClass('hidden');
+            }
+            return;
+        }
+
+        currentQuery = form.serialize();
+
+        if (previousQuery === currentQuery) {
+            return;
+        }
+
+        $('.order-by-group .active').removeClass('active');
+
+        if (window.history.pushState) {
+            q = encodeURIComponent($('input[type="search"]', form).val());
+
+            pathname = window.location.pathname;
+
+            if (pathname.indexOf('/app_dev.php') === 0) {
+                urlPrefix = '/app_dev.php';
+            } else if (pathname.indexOf('/app.php') === 0) {
+                urlPrefix = '/app.php';
+            } else {
+                urlPrefix = '';
+            }
+
+            url = urlPrefix + '/search/?q=' + q;
+            title = 'Search';
+
+            if (firstQuery) {
+                window.history.pushState(null, title, url);
+                firstQuery = false;
+            } else {
+                window.history.replaceState(null, title, url);
+            }
+        }
+
+        $.ajax({
+            url: form.attr('action'),
+            data: currentQuery,
+            success: showResults
+        });
+
+        searching = true;
+        previousQuery = currentQuery;
+    };
+
+    form.bind('keyup search', doSearch);
+
+    form.bind('keydown', function (event) {
+        var keymap,
+            currentSelected,
+            nextSelected;
+
+        keymap = {
+            enter: 13,
+            left: 37,
+            up: 38,
+            right: 39,
+            down: 40
+        };
+
+        if (keymap.up !== event.which && keymap.down !== event.which && keymap.enter !== event.which) {
+            return;
+        }
+
+        if ($('#search_query_query').val().match(/^\s*$/) !== null) {
+            document.activeElement.blur();
+            return;
+        }
+
+        event.preventDefault();
+
+        currentSelected = list.find('ul.packages li.selected');
+        nextSelected = (keymap.down === event.which) ? currentSelected.next('li') : currentSelected.prev('li');
+
+        if (currentSelected.length === 0 && keymap.down === event.which) {
+            currentSelected = list.find('ul.packages li:first').addClass('selected');
+        }
+
+        if (keymap.enter === event.which && currentSelected.data('url')) {
+            window.location = currentSelected.data('url');
+            return;
+        }
+
+        if (nextSelected.length > 0) {
+            currentSelected.removeClass('selected');
+            nextSelected.addClass('selected');
+
+            var elTop = nextSelected.position().top,
+                elHeight = nextSelected.height(),
+                windowTop = $(window).scrollTop(),
+                windowHeight = $(window).height();
+
+            if (elTop < windowTop) {
+                $(window).scrollTop(elTop);
+            } else if (elTop + elHeight > windowTop + windowHeight) {
+                $(window).scrollTop(elTop + elHeight + 20 - windowHeight);
+            }
+        }
+    });
+
+    // handle pressing S to focus the search
+    $(document.body).bind('keyup', function (event) {
+        if (event.which === 83 && (!document.activeElement || -1 === ['INPUT','SELECT','TEXTAREA'].indexOf(document.activeElement.tagName))) {
+            $('#search_query_query').focus();
+        }
+    });
+
+    if ($(document).width() >= 992) {
+        $('#search_query_query').focus();
+    }
+}(jQuery));
diff --git a/src/Packagist/WebBundle/Resources/public/js/submitPackage.js b/src/Packagist/WebBundle/Resources/public/js/submitPackage.js
new file mode 100644
index 0000000..d23877c
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/public/js/submitPackage.js
@@ -0,0 +1,52 @@
+(function ($) {
+    var showSimilarMax = 5;
+    var onSubmit = function(e) {
+        var success;
+        $('ul.package-errors, ul.similar-packages, div.confirmation', this).remove();
+        success = function (data) {
+            var html = '';
+            $('#submit').removeClass('loading');
+            if (data.status === 'error') {
+                $.each(data.reason, function (k, v) {
+                    html += '<li><div class="alert alert-warning">'+v+'</div></li>';
+                });
+                $('#submit-package-form').prepend('<ul class="list-unstyled package-errors">'+html+'</ul>');
+            } else {
+                if (data.similar.length) {
+                    var $similar = $('<ul class="list-unstyled similar-packages">');
+                    var limit = data.similar.length > showSimilarMax ? showSimilarMax : data.similar.length;
+                    for ( var i = 0; i < limit; i++ ) {
+                        var similar = data.similar[i];
+                        var $link = $('<a>').attr('href', similar.url).text(similar.name);
+                        $similar.append($('<li>').append($link))
+                    }
+                    if (limit != data.similar.length) {
+                        $similar.append($('<li>').text('And ' + (data.similar.length - limit) + ' more'));
+                    }
+                    $('#submit-package-form input[type="submit"]').before($('<div>').append(
+                        '<p><strong>Notice:</strong> One or more similarly named packages have already been submitted to Packagist. If this is a fork read the notice above regarding VCS Repositories.'
+                    ).append(
+                        '<p>Similarly named packages:'
+                    ).append($similar));
+                }
+                $('#submit-package-form input[type="submit"]').before(
+                    '<div class="confirmation">The package name found for your repository is: <strong>'+data.name+'</strong>, press Submit to confirm.</div>'
+                );
+                $('#submit').val('Submit');
+                $('#submit-package-form').unbind('submit');
+            }
+        };
+        $.post($(this).data('check-url'), $(this).serializeArray(), success);
+        $('#submit').addClass('loading');
+        e.preventDefault();
+    };
+
+    $('#package_repository').change(function() {
+        $('#submit-package-form').unbind('submit');
+        $('#submit-package-form').submit(onSubmit);
+        $('#submit').val('Check');
+    });
+
+    $('#package_repository').triggerHandler('change');
+})(jQuery);
+
diff --git a/src/Packagist/WebBundle/Resources/public/js/view.js b/src/Packagist/WebBundle/Resources/public/js/view.js
new file mode 100644
index 0000000..1304e1b
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/public/js/view.js
@@ -0,0 +1,181 @@
+/*jslint nomen: true, browser: true*/
+(function ($, humane) {
+    "use strict";
+
+    var versionCache = {},
+        ongoingRequest = false;
+
+    $('#add-maintainer').on('click', function (e) {
+        $('#remove-maintainer-form').addClass('hidden');
+        $('#add-maintainer-form').removeClass('hidden');
+        e.preventDefault();
+    });
+    $('#remove-maintainer').on('click', function (e) {
+        $('#add-maintainer-form').addClass('hidden');
+        $('#remove-maintainer-form').removeClass('hidden');
+        e.preventDefault();
+    });
+
+    $('.package .details-toggler').on('click', function () {
+        var target = $(this);
+
+        if (versionCache[target.attr('data-version-id')]) {
+            $('.package .version-details').html(versionCache[target.attr('data-version-id')]);
+        } else if (target.attr('data-load-more')) {
+            if (ongoingRequest) { // TODO cancel previous requests instead?
+                return;
+            }
+            ongoingRequest = true;
+            $('.package .version-details').addClass('loading');
+            $.ajax({
+                url: target.attr('data-load-more'),
+                dataType: 'json',
+                success: function (data) {
+                    versionCache[target.attr('data-version-id')] = data.content;
+                    ongoingRequest = false;
+                    $('.package .version-details')
+                        .removeClass('loading')
+                        .html(data.content);
+                }
+            });
+        }
+
+        $('.package .versions .open').removeClass('open');
+        target.addClass('open');
+    });
+
+    // initializer for #<version-id> present on page load
+    (function () {
+        var hash = document.location.hash;
+        if (hash.length > 1) {
+            hash = hash.substring(1);
+            $('.package .details-toggler[data-version-id="'+hash+'"]').click();
+        }
+    }());
+
+    function dispatchAjaxForm(form, success, className) {
+        var options = {
+            cache: false,
+            success: success,
+            data: $(form).serializeArray(),
+            type: $(form).attr('method'),
+            url: $(form).attr('action')
+        };
+        if ($(form).is('.' + className)) {
+            return;
+        }
+        $.ajax(options).complete(function () { $(form).removeClass(className); });
+        $(form).addClass(className);
+    }
+
+    function forceUpdatePackage(e, updateAll) {
+        var submit = $('input[type=submit]', '.package .force-update'), data;
+        if (e) {
+            e.preventDefault();
+        }
+        if (submit.is('.loading')) {
+            return;
+        }
+        data = $('.package .force-update').serializeArray();
+        if (updateAll) {
+            data.push({name: 'updateAll', value: '1'});
+        }
+        if (e && e.shiftKey) {
+            data.push({name: 'showOutput', value: '1'});
+        }
+        $.ajax({
+            url: $('.package .force-update').attr('action'),
+            dataType: 'json',
+            cache: false,
+            data: data,
+            type: $('.package .force-update').attr('method'),
+            success: function () {
+                document.location.reload(true);
+            },
+            context: $('.package .force-update')[0]
+        }).complete(function () { submit.removeClass('loading'); });
+        submit.addClass('loading');
+    }
+    $('.package .force-update').on('submit', forceUpdatePackage);
+    $('.package .force-update').on('click', forceUpdatePackage);
+    $('.package .mark-favorite').on('click', function (e) {
+        var options = {
+            dataType: 'json',
+            cache: false,
+            success: function () {
+                $(this).toggleClass('is-starred');
+            },
+            context: this
+        };
+        e.preventDefault();
+        if ($(this).is('.loading')) {
+            return;
+        }
+        if ($(this).is('.is-starred')) {
+            options.type = 'DELETE';
+            options.url = $(this).data('remove-url');
+        } else {
+            options.type = 'POST';
+            options.data = {"package": $(this).data('package')};
+            options.url = $(this).data('add-url');
+        }
+        $.ajax(options).complete(function () { $(this).removeClass('loading'); });
+        $(this).addClass('loading');
+    });
+    $('.package .delete').on('submit', function (e) {
+        e.preventDefault();
+        if (window.confirm('Are you sure?')) {
+            dispatchAjaxForm(this, function () {
+                humane.log('Package successfully deleted', {timeout: 0, clickToClose: true});
+                setTimeout(function () {
+                    document.location.href = document.location.href.replace(/\/[^\/]+$/, '/');
+                })
+            }, 'request-sent');
+        }
+    });
+    $('.package .delete-version .submit').on('click', function (e) {
+        e.preventDefault();
+        e.stopImmediatePropagation();
+        $(e.target).closest('form').submit();
+    });
+
+    $('.package .delete-version').on('submit', function (e) {
+        e.preventDefault();
+        e.stopImmediatePropagation();
+        var form = this;
+        if (window.confirm('Are you sure?')) {
+            dispatchAjaxForm(this, function () {
+                humane.log('Version successfully deleted', {timeout: 3000, clickToClose: true});
+                $(form).closest('.version').remove();
+            }, 'request-sent');
+        }
+    });
+    $('.package').on('click', '.requireme input', function () {
+        this.select();
+    });
+    if ($('.package').data('force-crawl')) {
+        forceUpdatePackage(null, true);
+    }
+
+    $('.readme a').on('click', function (e) {
+        var targetEl,
+            href = e.target.getAttribute('href');
+
+        if (href.substr(0, 1) === '#') {
+            targetEl = $(href);
+            if (targetEl.length) {
+                window.scrollTo(0, targetEl.offset().top - 70);
+                e.preventDefault();
+                e.stopImmediatePropagation();
+            }
+        }
+    });
+
+    var versionsList = $('.package .versions')[0];
+    if (versionsList.offsetHeight < versionsList.scrollHeight) {
+        $('.package .versions-expander').removeClass('hidden').on('click', function () {
+            $(this).addClass('hidden');
+            $(versionsList).css('max-height', 'inherit');
+        });
+    }
+}(jQuery, humane));
diff --git a/src/Packagist/WebBundle/Resources/translations/HWIOAuthBundle.en.yml b/src/Packagist/WebBundle/Resources/translations/HWIOAuthBundle.en.yml
new file mode 100644
index 0000000..d66b810
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/translations/HWIOAuthBundle.en.yml
@@ -0,0 +1 @@
+github: GitHub
diff --git a/src/Packagist/WebBundle/Resources/translations/messages.en.yml b/src/Packagist/WebBundle/Resources/translations/messages.en.yml
new file mode 100644
index 0000000..c1285c6
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/translations/messages.en.yml
@@ -0,0 +1,30 @@
+menu:
+    about_packagist: About Packagist
+    rss_feeds: Atom/RSS Feeds
+    about_composer: About Composer
+    home: Home
+    profile: Profile
+    logout: Logout
+    login: Login
+    register: Register
+    browse_packages: Browse Packages
+    twitter: Follow @packagist
+    github: GitHub
+    contact: Contact
+    stats: Statistics
+    api_doc: API
+
+link_type:
+    require: Requires
+    devRequire: Requires (Dev)
+    suggest: Suggests
+    conflict: Conflicts
+    replace: Replaces
+    provide: Provides
+
+'Search packages...': 'Search packages...'
+Sort: 'Sort'
+Order: 'Order'
+Username: 'Username'
+asc: 'asc'
+desc: 'desc'
diff --git a/src/Packagist/WebBundle/Resources/views/About/about.html.twig b/src/Packagist/WebBundle/Resources/views/About/about.html.twig
new file mode 100644
index 0000000..9bc624b
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/About/about.html.twig
@@ -0,0 +1,126 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block content %}
+    <h2 class="title">What is Packagist?</h2>
+    <p>Packagist is the default Composer package repository. It lets you find packages and lets Composer know where to get the code from. You can use Composer to manage your project or libraries' dependencies - read more about it on the <a href="https://getcomposer.org/">Composer website</a>.</p>
+    <p>You can find the packagist.org source on <a href="https://github.com/composer/packagist">GitHub</a>.</p>
+
+    <section class="row">
+        <div class="clearfix"></div>
+
+        <section class="col-md-6">
+            <h3 id="community">Community</h3>
+            <p>If you have questions about composer or want to help out, come and join us in the <em><a href="irc://irc.freenode.net/#composer">#composer</a></em> channel on irc.freenode.net. You can find more community resources in the <a href="https://getcomposer.org/doc/07-community.md">Composer documentation</a>.</p>
+        </section>
+
+        <section class="col-md-6">
+            <h3 id="community">Contributing / Donating</h3>
+            <p>To report issues or contribute code you can find the source repository on <a href="https://github.com/composer/packagist">GitHub</a>.</p>
+            <p>If you would like to financially support the hosting and maintenance of the project, the best way is to check out <a href="https://toranproxy.com">Toran Proxy</a> and buy a license. It can help you install packages fast and reliably, provides you with a private package repository, and the money goes towards Composer and Packagist maintenance!</p>
+        </section>
+    </section>
+
+    <h2 class="title" id="how-to-submit-packages">How to submit packages?</h2>
+    <section class="row">
+        <section class="col-md-6">
+            <h3 id="naming-your-package">Naming your package</h3>
+            <p>First of all, you must pick a package name. This is a very important step since it can not change and it should be unique enough to avoid conflicts in the future.</p>
+            <p>The package name consists of a vendor name and a project name joined by a <code>/</code>. The vendor name exists to prevent naming conflicts. For example, by including a vendor name both <code>igorw</code> and <code>seldaek</code> can have a library named <code>json</code> by naming their packages <code>igorw/json</code> and <code>seldaek/json</code>.</p>
+            <p>In some cases the vendor name and the package name may be identical. An example of this would be `monolog/monolog`. For projects with a unique name this is recommended. It also allows adding more related projects under the same vendor later on. If you are maintaining a library, this would make it really easy to split it up into smaller decoupled parts.</p>
+            <p>Here is a list of typical package names for reference:</p>
+            <pre><code>// Monolog is a library, so the vendor name and package name are the same.
+monolog/monolog
+
+// That could be the name of a drupal module (maintained/provided by monolog,
+// if the drupal team did it, the vendor would be drupal).
+monolog/monolog-drupal-module
+
+// Acme is a company or person here, they can name their package with a common name (Email).
+// As long as it's in their own vendor namespace it does not conflict with anyone else.
+acme/email</code></pre>
+            <p>Note that package names are case-insensitive, but it's encouraged to use a dash (-) as separator instead of CamelCased names.</p>
+        </section>
+
+        <section class="col-md-6">
+            <h3 id="creating-a-composerjson-file">Creating a composer.json file</h3>
+            <p>The composer.json file should reside at the top of your package's git/svn/.. repository, and is the way you describe your package to both packagist and composer.</p>
+            <p>A typical composer.json file looks like this:</p>
+            <pre><code>{
+    "name": "monolog/monolog",
+    "type": "library",
+    "description": "Logging for PHP 5.3",
+    "keywords": ["log","logging"],
+    "homepage": "https://github.com/Seldaek/monolog",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Jordi Boggiano",
+            "email": "j.boggiano@seld.be",
+            "homepage": "http://seld.be",
+            "role": "Developer"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "autoload": {
+        "psr-0": {
+            "Monolog": "src"
+        }
+    }
+}</code></pre>
+            <p>Most of this information is obvious, keywords are tags, require are list of dependencies that your package has. This can of course be packages, not only a php version. You can use ext-foo to require php extensions (e.g. ext-curl). Note that most extensions don't expose version information, so unless you know for sure it does, it's safer to use <code>"ext-curl": "*"</code> to allow any version of it. Finally the type field is in this case indicating that this is a library. If you do plugins for frameworks etc, and if they integrate composer, they may have a custom package type for their plugins that you can use to install the package with their own installer. In the absence of custom type, you can omit it or use "library".</p>
+            <p>Once you have this file committed in your repository root, you can <a href="{{ path('submit') }}">submit the package</a> to Packagist by entering the public repository URL.</p>
+        </section>
+
+        <section class="col-md-6">
+            <h3 id="managing-package-versions">Managing package versions</h3>
+            <p>New versions of your package are automatically fetched from tags you create in your VCS repository.</p>
+            <p>The easiest way to manage versioning is to just omit the version field from the composer.json file. The version numbers will then be parsed from the tag and branch names.</p>
+            <p>Tag/version names should match 'X.Y.Z', or 'vX.Y.Z', with an optional suffix for RC, beta, alpha or patch versions. Here are a few examples of valid tag names:</p>
+            <pre><code>1.0.0
+v1.0.0
+1.10.5-RC1
+v4.4.4beta2
+v2.0.0-alpha
+v2.0.4-p1</code></pre>
+            <p>Branches will automatically appear as "dev" versions that are easily installable by anyone that wants to try your library's latest and greatest, but that does not mean you should not tag releases. The use of <a href="http://semver.org/">Semantic Versioning</a> is strongly encouraged.</p>
+        </section>
+
+        <section class="col-md-6">
+            <h3 id="update-schedule">Update Schedule</h3>
+            <p>New packages will be crawled <strong>immediately</strong> after submission if you have JS enabled.</p>
+            <p>Existing packages without auto-updating (GitHub/BitBucket hook) will be crawled <strong>once a week</strong> for updates. When a hook is enabled packages are crawled whenever you push, or at least once a month in case the crawl failed. You can also trigger a manual update on your package page if you are logged-in as a maintainer.</p>
+            <p>It is highly recommended to set up the <strong>GitHub/BitBucket service hook</strong> for all your packages. This reduces the load on our side, and ensures your package is updated almost instantly. Check the how-to <a href="#how-to-update-packages">below</a>.</p>
+            <p>The search index is updated <strong>every five minutes</strong>. It will index (or reindex) any package that has been crawled since the last time the search indexer ran.</p>
+        </section>
+    </section>
+
+    <div class="clearfix"></div>
+
+    <h2 class="title" id="how-to-update-packages">How to update packages?</h2>
+    <section class="row">
+        <section class="col-md-6">
+            <h3>GitHub Service Hook</h3>
+            <p>Enabling the Packagist service hook ensures that your package will always be updated instantly when you push to GitHub. To do so you can go to your GitHub repository, click the "Settings" button, then "Webhooks &amp; Services". Add a "Packagist" service, and configure it with your API token, plus your Packagist username. Check the "Active" box and submit the form. You can then hit the "Test Service" button to trigger it and check if Packagist removes the warning about the package not being auto-updated.</p>
+        </section>
+
+        <section class="col-md-6">
+            <h3>Bitbucket POST Service</h3>
+            <p>To enable the Bitbucket service hook, go to your BitBucket repository, open the settings and select "Hooks" in the menu. Pick "POST" in the list and add it to your repository. Afterwards, you have to enter the Packagist endpoint, containing both your username and API token. Enter <code>https://packagist.org/api/bitbucket?username={{ app.user.username|default('USERNAME') }}&amp;apiToken=API_TOKEN</code> for the service's URL. Save your changes and you're done.</p>
+        </section>
+
+        <div class="clearfix"></div>
+        <section class="col-md-6">
+            <h3>Manual hook setup</h3>
+            <p>If you do not use Bitbucket or GitHub there is a generic endpoint you can call manually from a git post-receive hook or similar. You have to do a <code>POST</code> request to <code>https://packagist.org/api/update-package?username={{ app.user.username|default('USERNAME') }}&amp;apiToken=API_TOKEN</code> with a request body looking like this: <code>{"repository":{"url":"PACKAGIST_PACKAGE_URL"}}</code></p>
+            <p>You can do this using curl for example:</p>
+            <pre>curl -XPOST -H'content-type:application/json' 'https://packagist.org/api/update-package?username={{ app.user.username|default('USERNAME') }}&amp;apiToken=API_TOKEN' -d'{"repository":{"url":"PACKAGIST_PACKAGE_URL"}}'</pre>
+        </section>
+
+        <section class="col-md-6">
+            <h3>API Token</h3>
+            <p>You can find your API token on <a href="{{ path('fos_user_profile_show') }}">your profile page</a>.</p>
+        </section>
+    </section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/ApiDoc/index.html.twig b/src/Packagist/WebBundle/Resources/views/ApiDoc/index.html.twig
new file mode 100644
index 0000000..d761e91
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/ApiDoc/index.html.twig
@@ -0,0 +1,100 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+{% block content %}
+
+<h2 class="title">API documentation</h2>
+<p>Packagist has a public API to consume data. See bellow the most important URIs:</p>
+
+<ul>
+  <li><a href="#list-packages-by-organization">List packages by organization</a></li>
+  <li><a href="#search-packages-by-tag">Search packages by tag</a></li>
+  <li><a href="#search-packages-by-name">Search packages by name</a></li>
+  <li><a href="#get-package-by-name">Get a package by name</a></li>
+</ul>
+
+<section class="col-d-12">
+<h3 id="list-packages-by-organization"> List packages by organization </h3>
+<pre>
+GET https://packagist.drupal-composer.org/packages/list.json?vendor=[vendor]
+<code>
+{
+  "packageNames": [
+    "[vendor]/[package]",
+    ...
+  ]
+}
+</code></pre>
+</section>
+
+<section class="col-d-12">
+<h3 id="search-packages-by-tag">Search packages by tag</h3>
+<pre>
+GET https://packagist.drupal-composer.org/search.json?tags=[tag]
+<code>
+{
+  "results": [
+    {
+      "name": "[vendor]/[package]",
+      "description": "[description]",
+      "url": "https://packagist.drupal-composer.org/packages/[vendor]/[package]",
+      "repository": "[repository url]",
+      "downloads": [number of downloads],
+      "favers": [number of favers]
+    }
+    ...
+  ],
+  "total": [numbers of results]
+}
+</code>
+</section>
+
+
+<section class="col-d-12">
+<h3 id="search-packages-by-name">Search packages by name </h3>
+<pre>
+GET https://packagist.drupal-composer.org/search.json?q=[query]
+<code>
+{
+  "results" : [
+    {
+      "name": "[vendor]/[package]",
+      "description": "[description]",
+      "url": "https://packagist.drupal-composer.org/packages/[vendor]/[package]",
+      "repository": [repository url],
+      "downloads": [number of downloads],
+      "favers": [number of favers]
+    },
+    ...
+  ],
+  "total": [number of results],
+  "next": "https://packagist.drupal-composer.org/search.json?q=[query]&amp;page=[next page number]"
+}
+</code>
+</section>
+
+<section class="col-d-12">
+<h3 id="get-package-by-name">Get package by name </h3>
+<pre>
+GET https://packagist.drupal-composer.org/packages/[vendor]/[package].json
+<code>
+{
+  "package": {
+    "name": "[vendor]/[package],
+    "description": [description],
+    "time": [time of the last release],
+    "maintainers": [list of maintainers],
+    "versions": [list of versions and their dependencies, the same data of <a href="https://getcomposer.org/doc/01-basic-usage.md#package-versions">composer.json</a>]
+    "type": [package type],
+    "repository": [repository url],
+    "downloads": {
+      "total": [numbers of download],
+      "monthly": [numbers of download per month],
+      "daily": [numbers of download per day]
+    },
+    "favers": [number of favers]
+  }
+}
+</code>
+</section>
+
+
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Email/maintainer_added.txt.twig b/src/Packagist/WebBundle/Resources/views/Email/maintainer_added.txt.twig
new file mode 100644
index 0000000..3673eb8
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Email/maintainer_added.txt.twig
@@ -0,0 +1,7 @@
+{% autoescape false -%}
+
+You have been added to package {{ package_name }} as a maintainer.
+
+{{ url('view_package', { 'name': package_name }) }}
+
+{%- endautoescape %}
diff --git a/src/Packagist/WebBundle/Resources/views/Email/update_failed.txt.twig b/src/Packagist/WebBundle/Resources/views/Email/update_failed.txt.twig
new file mode 100644
index 0000000..408b49b
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Email/update_failed.txt.twig
@@ -0,0 +1,23 @@
+{% autoescape false -%}
+> Disclaimer: These email notifications are in experimental stage, if you find
+> that they look strange or contain false or misleading information please
+> reply to this email to let us know.
+
+The {{ package.name }} package of which you are a maintainer has
+failed to update due to invalid data contained in your composer.json.
+Please address this as soon as possible since the package stopped updating.
+
+It is recommended that you use `composer validate` to check for errors when you
+change your composer.json.
+
+Below is the full update log which should highlight errors as
+"Skipped branch ...":
+
+[{{ exception }}]: {{ exceptionMessage }}
+
+{{ details }}
+
+--
+If you do not wish to receive such emails in the future you can disable
+notifications on your profile page: https://packagist.drupal-composer.org/profile/edit
+{%- endautoescape %}
diff --git a/src/Packagist/WebBundle/Resources/views/Explore/explore.html.twig b/src/Packagist/WebBundle/Resources/views/Explore/explore.html.twig
new file mode 100644
index 0000000..01bcbca
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Explore/explore.html.twig
@@ -0,0 +1,36 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% import "PackagistWebBundle::macros.html.twig" as macros %}
+
+{% block content %}
+    {% block content_title %}<h2 class="title">Packages</h2>{% endblock %}
+    {% block lists %}
+    <section class="row">
+        <section class="packages-short col-lg-6">
+            <h3>New Releases <a href="{{ url('feed_releases', {_format: 'rss'}) }}">RSS</a></h3>
+
+            {{ macros.listPackagesShort(newlyReleased, true) }}
+        </section>
+
+        <section class="packages-short col-lg-6">
+            <h3>New Packages <a href="{{ url('feed_packages', {_format: 'rss'}) }}">RSS</a></h3>
+
+            {{ macros.listPackagesShort(newlySubmitted) }}
+        </section>
+
+        <div class="clearfix visible-lg-block"></div>
+
+        <section class="packages-short col-lg-6">
+            <h3>Popular Packages <a href="{{ path('browse_popular') }}">View All</a></h3>
+
+            {{ macros.listPackagesShort(popular, false, true) }}
+        </section>
+
+        <section class="packages-short col-lg-6">
+            <h3>Random Packages</h3>
+
+            {{ macros.listPackagesShort(random) }}
+        </section>
+    </section>
+    {% endblock %}
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Explore/popular.html.twig b/src/Packagist/WebBundle/Resources/views/Explore/popular.html.twig
new file mode 100644
index 0000000..090b86d
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Explore/popular.html.twig
@@ -0,0 +1,5 @@
+{% embed "PackagistWebBundle:Web:list.html.twig" %}
+    {% block content_title %}
+        <h2 class="title">Popular Packages</h2>
+    {% endblock %}
+{% endembed %}
diff --git a/src/Packagist/WebBundle/Resources/views/Feed/feeds.html.twig b/src/Packagist/WebBundle/Resources/views/Feed/feeds.html.twig
new file mode 100644
index 0000000..d502b94
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Feed/feeds.html.twig
@@ -0,0 +1,17 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block content %}
+    <h2 class="title">Atom/RSS Feeds</h2>
+
+    <h3>Global Feeds</h3>
+    <p>Newly Submitted Packages: <a href="{{ url('feed_packages', {_format: 'rss'}) }}">RSS</a>, <a href="{{ url('feed_packages', {_format: 'atom'}) }}">Atom</a></p>
+    <p>New Releases: <a href="{{ url('feed_releases', {_format: 'rss'}) }}">RSS</a>, <a href="{{ url('feed_releases', {_format: 'atom'}) }}">Atom</a></p>
+
+    <h3>Vendor Feed</h3>
+    <p>New Releases for a specific vendor namespace: <code>{{ url('feed_vendor', {vendor: 'XXX', _format: 'rss'})|replace({XXX: '%vendor%'}) }}</code></p>
+    <p>Replace <code>%vendor%</code> by the vendor name, and change rss to atom if you would like an atom feed.</p>
+
+    <h3>Package Feed</h3>
+    <p>New Releases for a specific package: <code>{{ url('feed_package', {package: 'X/X', _format: 'rss'})|replace({'X/X': '%vendor/package%'}) }}</code></p>
+    <p>Replace <code>%vendor/package%</code> by the package name, and change rss to atom if you would like an atom feed.</p>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/abandon.html.twig b/src/Packagist/WebBundle/Resources/views/Package/abandon.html.twig
new file mode 100644
index 0000000..91989ec
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/abandon.html.twig
@@ -0,0 +1,24 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block title %}{{ package.name }} - {{ parent() }}{% endblock %}
+
+{% block content %}
+<h2 class="title">
+    <a href="{{ path("view_vendor", {vendor: package.vendor}) }}">{{ package.vendor }}/</a><a href="{{ path("view_package", {name: package.name}) }}">{{ package.packageName }}</a>
+</h2>
+
+<section class="row">
+    {{ form_start(form, { attr: { class: 'col-sm-6' } }) }}
+        {{ form_widget(form) }}
+
+        <input class="btn btn-block btn-default btn-lg" type="submit" value="Abandon Package" />
+    {{ form_end(form) }}
+
+    <div class="col-sm-6">
+        <p>You are about to mark this package as abandoned. This will alert users that this package will no longer be maintained.</p>
+        <p>If you are handing this project over to a new maintainer or know of a package that replaces it, please use
+            the field below to point users to the new package. In case you cannot point them to a new package, this one
+            will be tagged as abandoned and a replacement can be added later.</p>
+    </div>
+</section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/browse.html.twig b/src/Packagist/WebBundle/Resources/views/Package/browse.html.twig
new file mode 100644
index 0000000..dc44252
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/browse.html.twig
@@ -0,0 +1,16 @@
+{% extends "PackagistWebBundle:Web:list.html.twig" %}
+
+{% set filters = [] %}
+
+{% if type %}{% set filters = filters|merge(["of type #{type|join(' or ')}"]) %}{% endif %}
+{% if tag %}{% set filters = filters|merge(["tagged with #{tag|join(' or ')}"]) %}{% endif %}
+
+{% block content_title %}
+    <h1>Packages {{ filters|join(' and ') }}</h1>
+{% endblock %}
+
+{# TODO:
+
+- Add browsing by tag, type, most required, ..
+
+#}
\ No newline at end of file
diff --git a/src/Packagist/WebBundle/Resources/views/Package/dependents.html.twig b/src/Packagist/WebBundle/Resources/views/Package/dependents.html.twig
new file mode 100644
index 0000000..b2c55b2
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/dependents.html.twig
@@ -0,0 +1,29 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% set showSearchDesc = 'hide' %}
+
+{% block head_additions %}<meta name="robots" content="noindex, nofollow">{% endblock %}
+
+{% block title %}Dependent Packages - {{ name }} - {{ parent() }}{% endblock %}
+
+{% block content %}
+    <div class="row">
+        <div class="col-xs-12 package">
+            <div class="package-header">
+                <h2 class="title">
+                    <a href="{{ path("view_package", {name: name}) }}">{{ name }}</a> dependents
+                    <small>({{ count }})</small>
+                </h2>
+            </div>
+        </div>
+    </div>
+
+    <section class="row">
+        <section class="col-md-12">
+        {% embed "PackagistWebBundle:Web:list.html.twig" with {noLayout: 'true', showAutoUpdateWarning: false} %}
+            {% block content_title %}
+            {% endblock %}
+        {% endembed %}
+        </section>
+    </section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/edit.html.twig b/src/Packagist/WebBundle/Resources/views/Package/edit.html.twig
new file mode 100644
index 0000000..af08529
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/edit.html.twig
@@ -0,0 +1,17 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block title %}
+    Edit Package {{ package.name }}
+{% endblock %}
+
+{% block content %}
+<h2 class="title">Edit {{ package.name }}</h2>
+
+<section class="row">
+    {{ form_start(form, { attr: { class: 'col-md-6' } }) }}
+        {{ form_widget(form) }}
+
+        <input class="btn btn-block btn-primary btn-lg" id="submit" type="submit" value="Update" />
+    {{ form_end(form) }}
+</section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/providers.html.twig b/src/Packagist/WebBundle/Resources/views/Package/providers.html.twig
new file mode 100644
index 0000000..53486a6
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/providers.html.twig
@@ -0,0 +1,3 @@
+{% extends "PackagistWebBundle:Web:list.html.twig" %}
+
+{% block content_title %}<h1>The following packages provide {{ name }}</h1>{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/stats.html.twig b/src/Packagist/WebBundle/Resources/views/Package/stats.html.twig
new file mode 100644
index 0000000..469b55f
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/stats.html.twig
@@ -0,0 +1,87 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% set showSearchDesc = 'hide' %}
+
+{% block head_additions %}<meta name="robots" content="noindex, nofollow">{% endblock %}
+
+{% block title %}Install Statistics - {{ package.name }} - {{ parent() }}{% endblock %}
+
+{% block content %}
+    <div class="row">
+        <div class="col-xs-12 package">
+            <div class="package-header">
+                <h2 class="title">
+                    <a href="{{ path("view_package", {name: package.name}) }}">{{ package.name }}</a> install statistics
+                </h2>
+            </div>
+        </div>
+    </div>
+
+    <section class="row package-installs">
+        <div class="col-lg-12">
+            <h3>Installs</h3>
+
+            <div class="row">
+                <div class="col-md-4 col-xs-12">
+                    <dl class="dl-horizontal">
+                        <dt class="font-normal">Overall</dt>
+                        <dd class="font-normal">{{ downloads.total|number_format(0, '.', '&#8201;')|raw }}</dd>
+                    </dl>
+                </div>
+                <div class="col-md-4 col-xs-12">
+                    <dl class="dl-horizontal">
+                        <dt class="font-normal">Last 30 days</dt>
+                        <dd class="font-normal">{{ downloads.monthly|number_format(0, '.', '&#8201;')|raw }}</dd>
+                    </dl>
+                </div>
+                <div class="col-md-4 col-xs-12">
+                    <dl class="dl-horizontal">
+                        <dt class="font-normal">Today<br><small>(since midnight, UTC)</small></dt>
+                        <dd class="font-normal">{{ downloads.daily|number_format(0, '.', '&#8201;')|raw }}</dd>
+                    </dl>
+                </div>
+            </div>
+
+            <h3>Daily installs{% if average != 'daily' %}, averaged {{ average }}{% endif %}</h3>
+            <div class="row">
+                <div class="col-xs-12">
+                    <svg class="chart js-all-dls" width="500" height="200">
+                        Sorry, the graph can't be displayed because your browser doesn't support &lt;svg&gt; html element.
+                    </svg>
+                </div>
+            </div>
+
+            <br>
+            <h3>Daily installs per version{% if average != 'daily' %}, averaged {{ average }}{% endif %}</h3>
+            <div class="row package version-stats">
+                <div class="col-xs-12 col-md-9 version-stats-chart">
+                    <div style="position: relative">
+                        <svg class="chart js-version-dls" width="500" height="200">
+                            Sorry, the graph can't be displayed because your browser doesn't support &lt;svg&gt; html element.
+                        </svg>
+                    </div>
+                </div>
+                {% include 'PackagistWebBundle:Package:versionList.html.twig' with {versions: versions, expandedId: expandedId} %}
+            </div>
+        </div>
+    </section>
+{% endblock %}
+
+{% block stylesheets %}
+    <link href="https://cdn.rawgit.com/novus/nvd3/v1.7.1/build/nv.d3.css" rel="stylesheet">
+{% endblock %}
+
+{% block scripts %}
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
+    <script src="https://cdn.rawgit.com/novus/nvd3/v1.7.1/build/nv.d3.min.js"></script>
+    <script src="{{ asset('bundles/packagistweb/js/charts.js') }}"></script>
+    <script>
+        (function () {
+            var average = {{ average|json_encode|raw }};
+            var date = {{ date|json_encode|raw }};
+            var versions = {{ versions|json_encode|raw }};
+
+            initPackageStats(average, date, versions, {{ path('package_stats', {name: package.name})|json_encode|raw }}, {{ path('version_stats', {name: package.name, version: '_VERSION_'})|json_encode|raw }});
+        }());
+    </script>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/submitPackage.html.twig b/src/Packagist/WebBundle/Resources/views/Package/submitPackage.html.twig
new file mode 100644
index 0000000..acf6ec1
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/submitPackage.html.twig
@@ -0,0 +1,27 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block scripts %}
+    <script src="{{ asset('bundles/packagistweb/js/submitPackage.js')}}"></script>
+{% endblock %}
+
+{% set showSearchDesc = 'hide' %}
+
+{% block content %}
+<h2 class="title">Submit package</h2>
+
+<section class="row">
+    {{ form_start(form, { attr: { class: 'col-md-6', 'data-check-url': path('submit.fetch_info'), id: 'submit-package-form' } }) }}
+        {{ form_widget(form) }}
+
+        {{ form_rest(form) }}
+
+        <input class="btn btn-block btn-success btn-lg" id="submit" type="submit" value="Submit" />
+    {{ form_end(form) }}
+
+    <div class="col-md-6">
+        <p>Please make sure you have read the package <a href="{{ path('about') }}#naming-your-package">naming conventions</a> before submitting your package. The authoritative name of your package will be taken from the composer.json file inside the master branch or trunk of your repository, and it can not be changed after that.</p>
+        <p><strong>Do not submit forks of existing packages.</strong> If you need to test changes to a package that you forked to patch, use <a href="https://getcomposer.org/doc/05-repositories.md#vcs">VCS Repositories</a> instead. If however it is a real long-term fork you intend on maintaining feel free to submit it.</p>
+        <p>If you need help or if you have any questions please get in touch with the Composer <a href="https://getcomposer.org/doc/07-community.md">community</a>.</p>
+    </div>
+</section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/versionDetails.html.twig b/src/Packagist/WebBundle/Resources/views/Package/versionDetails.html.twig
new file mode 100644
index 0000000..6783ce5
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/versionDetails.html.twig
@@ -0,0 +1,65 @@
+{% import "PackagistWebBundle::macros.html.twig" as packagist %}
+<div class="title">
+    <span class="version-number">
+        {{- version.version -}}
+        {% if version.hasVersionAlias() %}
+            / {{ version.versionAlias }}
+        {%- endif -%}
+    </span>
+
+    <span class="release-date">{{ version.releasedAt|date("Y-m-d H:i") }} UTC</span>
+</div>
+
+<div class="clearfix package-links">
+    {% for types in [["require", "devRequire", "suggest", "provide", "conflict", "replace"]] %}
+        <div class="row">
+            {% for type in types %}
+                {% if loop.index0 is even %}<div class="clearfix visible-sm-block"></div>{% endif %}
+                {% if loop.index0 % 3 == 0 %}<div class="clearfix visible-md-block visible-lg-block"></div>{% endif %}
+                <div class="{{ type }}s col-sm-6 col-md-4 {% if not attribute(version, type)|length %}hidden-xs{% endif %}">
+                    <p class="link-type">{{ ('link_type.' ~ type)|trans }}</p>
+                    {% if attribute(version, type)|length %}
+                        <ul>
+                            {%- for link in attribute(version, type) -%}
+                                <li>{{ packagist.packageLink(link.packageName, type) }}: {{ link.packageVersion == 'self.version' ? version.version : link.packageVersion }}</li>
+                            {%- endfor -%}
+                        </ul>
+                    {% else %}
+                        <p class="no-links">None</p>
+                    {% endif %}
+                </div>
+            {% endfor %}
+        </div>
+    {% endfor %}
+</div>
+
+<div class="metadata">
+    <p class="license"><i class="glyphicon glyphicon-copyright-mark" title="License"></i> {% if not version.license %}<span class="unknown">{% endif %}{{ version.license ? version.license|join(', ') : 'Unknown License' }}{% if not version.license %}</span>{% endif %} <span class="source-reference"><i class="glyphicon glyphicon-bookmark" title="Source Reference"></i> {{ version.source.reference }}</span></p>
+
+    {% if version.authors|length %}
+        <i class="glyphicon glyphicon-user" title="Authors"></i>
+        <ul class="authors">
+            {% for author in version.authors %}
+                <li>
+                    {%- if author.homepage -%}
+                        <a href="{{ author.homepage }}">{{ author.name }}</a>
+                    {%- else -%}
+                        {{ author.name }}
+                    {%- endif -%}
+                    {% if author.email %}
+                        <span class="visible-sm-inline visible-md-inline visible-lg-inline">&lt;{{ author.email|split('@', 2)[0] }}<span style="display:none">.woop</span>@{{ author.email|split('@', 2)[1] }}&gt;</span>
+                    {%- endif -%}
+                </li>
+            {% endfor %}
+        </ul>
+    {% endif %}
+
+    {% if version.tags|length %}
+        <p class="tags">
+            <i class="glyphicon glyphicon-tag" title="Tags"></i>
+            {% for tag in version.tags -%}
+                <a href="{{ url("search", {tags: tag.name}) }}">{{ tag.name }}</a>
+            {%- endfor -%}
+        </p>
+    {% endif %}
+</div>
diff --git a/src/Packagist/WebBundle/Resources/views/Package/versionList.html.twig b/src/Packagist/WebBundle/Resources/views/Package/versionList.html.twig
new file mode 100644
index 0000000..f76597e
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/versionList.html.twig
@@ -0,0 +1,25 @@
+<div class="col-md-3 package-aside versions-wrapper">
+    <ul class="versions">
+        {% for version in versions %}
+            {% set expanded = version.id == expandedId|default(false) %}
+            <li class="details-toggler version{% if loop.last %} last{% endif %}{% if expanded %} open{% endif %}" data-version-id="{{ version.version }}" data-load-more="{{ path('view_version', {versionId: version.id, _format: 'json'}) }}">
+                <a href="#{{ version.version }}" class="version-number">
+                    {{- version.version -}}
+                    {% if version.hasVersionAlias() %}
+                        / {{ version.versionAlias }}
+                    {% endif -%}
+                </a>
+
+                {% if deleteVersionCsrfToken is defined and deleteVersionCsrfToken is not empty %}
+                <form class="delete-version" action="{{ path("delete_version", {"versionId": version.id}) }}" method="DELETE">
+                    <input type="hidden" name="_token" value="{{ deleteVersionCsrfToken }}" />
+                    <i class="submit glyphicon glyphicon-remove"></i>
+                </form>
+                {% endif %}
+            </li>
+        {% endfor %}
+    </ul>
+    <div class="hidden versions-expander">
+        <i class="glyphicon glyphicon-chevron-down"></i>
+    </div>
+</div>
diff --git a/src/Packagist/WebBundle/Resources/views/Package/viewPackage.html.twig b/src/Packagist/WebBundle/Resources/views/Package/viewPackage.html.twig
new file mode 100644
index 0000000..11b4cb4
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/viewPackage.html.twig
@@ -0,0 +1,244 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% set showSearchDesc = 'hide' %}
+
+{% block title %}{{ package.name }} - {{ parent() }}{% endblock %}
+
+{% block head_feeds %}
+    <link rel="alternate" type="application/rss+xml" title="New Releases - {{ package.name }}" href="{{ url('feed_package', {package: package.name, _format: 'rss'}) }}" />
+    <link rel="alternate" type="application/rss+xml" title="New Releases - {{ package.vendor }}" href="{{ url('feed_vendor', {vendor: package.vendor, _format: 'rss'}) }}" />
+    {{ parent() }}
+{% endblock %}
+
+{% block head_additions %}
+    <script type="text/javascript">
+      WebFontConfig = {
+        google: { families: [ 'Source+Code+Pro::latin' ] }
+      };
+      (function() {
+        var wf = document.createElement('script');
+        wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
+        wf.type = 'text/javascript';
+        wf.async = 'true';
+        var s = document.getElementsByTagName('script')[0];
+        s.parentNode.insertBefore(wf, s);
+      })();
+    </script>
+{% endblock %}
+
+{% block scripts %}
+    <script src="{{ asset('bundles/packagistweb/js/view.js')}}"></script>
+{% endblock %}
+
+{% block description -%}
+    {{ package.description }}
+{%- endblock %}
+
+{% set hasActions = is_granted('ROLE_EDIT_PACKAGES') or is_granted('ROLE_EDIT_PACKAGES') or package.maintainers.contains(app.user) %}
+
+{% block content %}
+    <div class="row">
+        <div class="col-xs-12 package"{% if app.user and package.crawledAt is null and (is_granted('ROLE_EDIT_PACKAGES') or package.maintainers.contains(app.user)) %} data-force-crawl="true"{% endif %}>
+            <div class="package-header">
+
+                <div class="row">
+                    <div class="col-md-9">
+                        <h2 class="title">
+                            {% if is_favorite is defined %}
+                                <i class="mark-favorite glyphicon glyphicon-star {% if is_favorite %}is-starred{% endif %}" data-remove-url="{{ path('user_remove_fav', {name: app.user.username, package: package.name}) }}" data-add-url="{{ path('user_add_fav', {name: app.user.username}) }}" data-package="{{ package.name }}"></i>
+                            {% endif %}
+                            <a href="{{ path("view_vendor", {"vendor": package.vendor}) }}">{{ package.vendor }}/</a>{{ package.packageName }}
+                        </h2>
+                    </div>
+                </div>
+            </div>
+
+            <div class="row">
+                <div class="col-md-8">
+                    <p class="requireme"><i class="glyphicon glyphicon-save"></i> <input type="text" readonly="readonly" value="composer require {{ "#{package.vendor}/#{package.packageName}" }}" /></p>
+
+                    {% if not package.autoUpdated and app.user and (package.maintainers.contains(app.user) or is_granted('ROLE_UPDATE_PACKAGES')) %}
+                        {% if "github.com" in package.repository %}
+                            <div class="alert alert-danger">This package is not auto-updated. Please set up the <a href="{{ path('fos_user_profile_show') }}">GitHub Service Hook</a> for Packagist so that it gets updated whenever you push!</div>
+                        {% elseif "bitbucket.org" in package.repository %}
+                            <div class="alert alert-danger">This package is not auto-updated. Please set up the <a href="{{ path('fos_user_profile_show') }}">BitBucket POST Service</a> for Packagist so that it gets updated whenever you push!</div>
+                        {% endif %}
+                    {% endif %}
+
+                    {% if package.abandoned %}
+                        <div class="alert alert-danger">
+                            This package is <strong>abandoned</strong> and no longer maintained.
+                            {% if package.replacementPackage is not empty %}
+                                The author suggests using the <a href="{{ '://' in package.replacementPackage ? package.replacementPackage : path('view_package', {name: package.replacementPackage}) }}">{{ package.replacementPackage }}</a> package instead.
+                            {% else %}
+                                No replacement package was suggested.
+                                {% if (is_granted('ROLE_EDIT_PACKAGES') or package.maintainers.contains(app.user)) %}
+                                    <a href="{{ path('abandon_package', {name: package.name}) }}">Suggest a replacement.</a>
+                                {% endif %}
+                            {% endif %}
+                        </div>
+                    {% endif %}
+                    {% if package.updateFailureNotified
+                        and app.user and (package.maintainers.contains(app.user) or is_granted('ROLE_UPDATE_PACKAGES'))
+                    %}
+                        <div class="alert alert-danger">This package is in a broken state and will not update anymore. Some branches contain invalid data and until you fix them the entire package is frozen. Click "Force Update" above to see details.</div>
+                    {% endif %}
+
+                    {% if expandedVersion and not expandedVersion.license %}
+                        <div class="alert alert-danger">There is no license information available for the latest version ({{ expandedVersion.version }}) of this package.</div>
+                    {% endif %}
+
+                    <p class="description">{{ package.description }}</p>
+
+                    {% if hasActions %}
+                        <div class="btn-group btn-group-xs">
+                            {% if (is_granted('ROLE_EDIT_PACKAGES') or package.maintainers.contains(app.user)) and not package.abandoned %}
+                                <form class="action abandon" action="{{ path('abandon_package', {name: package.name}) }}">
+                                    <input class="btn btn-warning" type="submit" value="Abandon" />
+                                </form>
+                            {% endif %}
+                            {% if (is_granted('ROLE_EDIT_PACKAGES') or package.maintainers.contains(app.user)) and package.abandoned %}
+                                <form class="action un-abandon" action="{{ path('unabandon_package', {name: package.name}) }}">
+                                    <input class="btn btn-default" type="submit" value="Un-abandon" />
+                                </form>
+                            {% endif %}
+                            {% if deleteForm is defined %}
+                                <form class="delete action" action="{{ path('delete_package', {name: package.name}) }}" method="DELETE">
+                                    {{ form_widget(deleteForm._token) }}
+                                    <input class="btn btn-danger" type="submit" value="Delete" />
+                                </form>
+                            {% endif %}
+                            {% if is_granted('ROLE_UPDATE_PACKAGES') or package.maintainers.contains(app.user) %}
+                                <form class="force-update action" action="{{ path('update_package', {name: package.name}) }}" method="PUT">
+                                    <input type="hidden" name="update" value="1" />
+                                    <input class="btn btn-success" type="submit" value="Update" />
+                                </form>
+                            {% endif %}
+                            {% if is_granted('ROLE_EDIT_PACKAGES') or package.maintainers.contains(app.user) %}
+                                <form class="action" action="{{ path("edit_package", {name: package.name}) }}">
+                                    <input class="btn btn-primary" type="submit" value="Edit" />
+                                </form>
+                            {% endif %}
+                        </div>
+                    {% endif %}
+                </div>
+
+                <div class="col-md-offset-1 col-md-3">
+                    <div class="row package-aside">
+                        <div class="details col-xs-12 col-sm-6 col-md-12">
+                            <p class="maintainers">
+                                {% for maintainer in package.maintainers -%}
+                                    <a href="{{ path('user_profile', {'name': maintainer.username}) }}"><img width="48" height="48" title="{{ maintainer.username }}" src="//www.gravatar.com/avatar/{{ maintainer.email|gravatar_hash }}?s=48&amp;d=identicon"></a>
+                                {% endfor %}
+                                {% if addMaintainerForm is defined or removeMaintainerForm is defined %}
+                                    {% if removeMaintainerForm is defined %}<a title="Remove Maintainer" id="remove-maintainer" href="{{ path('remove_maintainer', {'name': package.name}) }}"><i class="glyphicon glyphicon-remove"></i></a>{% endif %}
+                                    {% if addMaintainerForm is defined %}<a title="Add Maintainer" id="add-maintainer" href="{{ path('add_maintainer', {'name': package.name}) }}"><i class="glyphicon glyphicon-plus"></i></a>{% endif %}
+                                {% endif %}
+                            </p>
+
+                            {% set repoUrl = package.repository|replace({'git://github.com/': 'https://github.com/', 'git@github.com:': 'https://github.com/'}) %}
+                            <p class="canonical">
+                                <a href="{{ repoUrl }}" title="Canonical Repository URL">{{ repoUrl|replace({'https://':'', 'http://':''}) }}</a>
+                            </p>
+
+                            {% if version and version.homepage %}
+                                <p><a href="{{ version.homepage }}">Homepage</a></p>
+                            {% endif %}
+                            {% if version.support.source is defined %}
+                                <p><a href="{{ version.support.source }}">Source</a></p>
+                            {% endif %}
+                            {% if version and version.support.issues is defined %}
+                                <p><a href="{{ version.support.issues }}">Issues</a></p>
+                            {% endif %}
+                            {% if version and version.support.irc is defined %}
+                                <p><a href="{{ version.support.irc }}">IRC</a></p>
+                            {% endif %}
+                            {% if version and version.support.forum is defined %}
+                                <p><a href="{{ version.support.forum }}">Forum</a></p>
+                            {% endif %}
+                            {% if version and version.support.wiki is defined %}
+                                <p><a href="{{ version.support.wiki }}">Wiki</a></p>
+                            {% endif %}
+                            {% if version and version.support.docs is defined %}
+                                <p><a href="{{ version.support.docs }}">Documentation</a></p>
+                            {% endif %}
+                        </div>
+
+                        <div class="facts col-xs-12 col-sm-6 col-md-12">
+                            <p><span><a href="{{ path('view_package_stats', {name: package.name}) }}" rel="nofollow">Installs</a>:</span> {% if downloads.total is defined %}{{ downloads.total|number_format(0, '.', '&#8201;')|raw }}{% else %}N/A{% endif %}</p>
+                            {% if dependents is defined %}<p><span><a href="{{ path('view_package_dependents', {name: package.name}) }}" rel="nofollow">Dependents</a>:</span> {{ dependents|number_format(0, '.', '&#8201;')|raw }}</p>{% endif %}
+                            {% if package.gitHubStars is not null %}<p><span>Stars:</span> {{ package.gitHubStars|number_format(0, '.', '&#8201;')|raw }}</p>{% endif %}
+                            {% if package.gitHubWatches is not null %}<p><span>Watchers:</span> {{ package.gitHubWatches|number_format(0, '.', '&#8201;')|raw }}</p>{% endif %}
+                            {% if package.gitHubForks is not null %}<p><span>Forks:</span> {{ package.gitHubForks|number_format(0, '.', '&#8201;')|raw }}</p>{% endif %}
+                            {% if package.gitHubOpenIssues is not null %}<p><span>Open Issues:</span> {{ package.gitHubOpenIssues|number_format(0, '.', '&#8201;')|raw }}</p>{% endif %}
+                            {% if package.language is not empty and package.language != 'PHP' %}<p><span>Language:</span> {{ package.language }}</p>{% endif %}
+                            {% if package.type is not empty and package.type != 'library' %}<p><span>Type:</span> {{ package.type }}</p>{% endif %}
+                        </div>
+                    </div>
+                </div>
+            </div>
+
+            {% if addMaintainerForm is defined or removeMaintainerForm is defined %}
+                <div class="row">
+                    {% if addMaintainerForm is defined %}
+                        {{ form_start(addMaintainerForm, {
+                            attr: { id: 'add-maintainer-form', class: 'col-sm-6 col-md-3 col-md-offset-9 ' ~ (show_add_maintainer_form|default(false) ? '': 'hidden') },
+                            action: path('add_maintainer', { 'name': package.name })
+                        }) }}
+                            <div>
+                                <h4>Add Maintainer</h4>
+                                <p>
+                                    {{ form_label(addMaintainerForm.user, "Username") }}
+                                    {{ form_errors(addMaintainerForm.user) }}
+                                    {{ form_widget(addMaintainerForm.user) }}
+                                </p>
+                                {{ form_rest(addMaintainerForm) }}
+                                <input class="btn btn-block btn-success btn-lg" type="submit" value="Add Maintainer" />
+                            </div>
+                        {{ form_end(addMaintainerForm) }}
+                    {% endif %}
+
+                    {% if removeMaintainerForm is defined %}
+                        {{ form_start(removeMaintainerForm, {
+                            attr: { id: 'remove-maintainer-form', class: 'col-sm-6 col-md-3 col-md-offset-9 ' ~ (show_remove_maintainer_form|default(false) ? '': 'hidden') },
+                            action: path('remove_maintainer', { 'name': package.name })
+                        }) }}
+                            <div>
+                                <h4>Remove Maintainer</h4>
+                                <p>
+                                    {{ form_label(removeMaintainerForm.user, "Username") }}
+                                    {{ form_errors(removeMaintainerForm.user) }}
+                                    {{ form_widget(removeMaintainerForm.user) }}
+                                </p>
+                                {{ form_rest(removeMaintainerForm) }}
+                                <input class="btn btn-block btn-danger btn-lg" type="submit" value="Remove Maintainer" />
+                            </div>
+                        {{ form_end(removeMaintainerForm) }}
+                    {% endif %}
+                </div>
+            {% endif %}
+
+            {% if versions|length %}
+            <div class="row versions-section">
+                <div class="version-details col-md-9">
+                    {% if expandedVersion %}
+                        {% include 'PackagistWebBundle:Package:versionDetails.html.twig' with {version: expandedVersion} %}
+                    {% endif %}
+                </div>
+                {% include 'PackagistWebBundle:Package:versionList.html.twig' with {versions: versions, expandedId: expandedVersion.id, deleteVersionCsrfToken: deleteVersionCsrfToken|default(null)} %}
+            {% elseif package.crawledAt is null %}
+                <p class="col-xs-12">This package has not been crawled yet, some information is missing.</p>
+            {% else %}
+                <p class="col-xs-12">This package has no released version yet, and little information is available.</p>
+            {% endif %}
+        </div>
+
+        {% if package.readme != null %}
+            <hr class="clearfix">
+            <div class="readme">
+                <h1>README</h1>
+                {{ package.readme|raw }}
+            </div>
+        {% endif %}
+    </div>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Package/viewVendor.html.twig b/src/Packagist/WebBundle/Resources/views/Package/viewVendor.html.twig
new file mode 100644
index 0000000..7c48b1b
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Package/viewVendor.html.twig
@@ -0,0 +1,8 @@
+{% extends "PackagistWebBundle:Web:list.html.twig" %}
+
+{% block head_feeds %}
+    <link rel="alternate" type="application/rss+xml" title="New Releases - {{ vendor }}" href="{{ url('feed_vendor', {vendor: vendor, _format: 'rss'}) }}" />
+    {{ parent() }}
+{% endblock %}
+
+{% block content_title %}<h1>Packages from {{ vendor }}</h1>{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/User/favorites.html.twig b/src/Packagist/WebBundle/Resources/views/User/favorites.html.twig
new file mode 100644
index 0000000..4c540d1
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/User/favorites.html.twig
@@ -0,0 +1,35 @@
+{% extends "PackagistWebBundle:User:packages.html.twig" %}
+
+{% import "PackagistWebBundle::macros.html.twig" as macros %}
+
+{% set isActualUser = app.user and app.user.username is sameas(user.username) %}
+
+{% block content %}
+<h2 class="title">
+    {{ user.username }}
+    <small>
+        {%- if not isActualUser %}
+            member since: {{ user.createdAt|date('M d, Y') }}
+            {%- if is_granted('ROLE_ADMIN') %}
+                <a href="mailto:{{ user.email }}">{{ user.email }}</a>
+            {%- endif %}
+        {%- endif %}
+    </small>
+</h2>
+
+<section class="row">
+    {% if isActualUser %}
+        <section class="col-md-3">
+            {{ knp_menu_render('profile_menu', {currentClass: 'active', allow_safe_labels: true}) }}
+        </section>
+    {% endif %}
+
+    <section class="{{ isActualUser ? 'col-md-9' : 'col-md-12' }}">
+        {% embed "PackagistWebBundle:Web:list.html.twig" with {noLayout: 'true', showAutoUpdateWarning: isActualUser} %}
+            {% block content_title %}
+                <h3 class="font-normal profile-title">{{ isActualUser ? 'My' : user.username~'\'s' }} favorite packages</h3>
+            {% endblock %}
+        {% endembed %}
+    </section>
+</section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/User/packages.html.twig b/src/Packagist/WebBundle/Resources/views/User/packages.html.twig
new file mode 100644
index 0000000..260060b
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/User/packages.html.twig
@@ -0,0 +1,35 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% import "PackagistWebBundle::macros.html.twig" as macros %}
+
+{% set isActualUser = app.user and app.user.username is sameas(user.username) %}
+
+{% block content %}
+<h2 class="title">
+    {{ user.username }}
+    <small>
+        {%- if not isActualUser %}
+            member since: {{ user.createdAt|date('M d, Y') }}
+            {%- if is_granted('ROLE_ADMIN') %}
+                <a href="mailto:{{ user.email }}">{{ user.email }}</a>
+            {%- endif %}
+        {%- endif %}
+    </small>
+</h2>
+
+<section class="row">
+    {% if isActualUser %}
+        <section class="col-md-3">
+            {{ knp_menu_render('profile_menu', {'currentClass': 'active', 'allow_safe_labels': true}) }}
+        </section>
+    {% endif %}
+
+    <section class="{{ isActualUser ? 'col-md-9' : 'col-md-12' }}">
+        {% embed "PackagistWebBundle:Web:list.html.twig" with {noLayout: 'true', showAutoUpdateWarning: isActualUser} %}
+            {% block content_title %}
+                <h3 class="font-normal profile-title">{{ isActualUser ? 'My packages' : 'Packages maintained by ' ~ user.username }}</h3>
+            {% endblock %}
+        {% endembed %}
+    </section>
+</section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/User/profile.html.twig b/src/Packagist/WebBundle/Resources/views/User/profile.html.twig
new file mode 100644
index 0000000..b538264
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/User/profile.html.twig
@@ -0,0 +1,27 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% import "PackagistWebBundle::macros.html.twig" as macros %}
+
+{% set isActualUser = app.user and app.user.username is sameas(user.username) %}
+
+{% block content %}
+<h2 class="title">
+    {{ user.username }}
+    <small>
+        member since: {{ user.createdAt|date('M d, Y') }}
+        {%- if is_granted('ROLE_ADMIN') %}
+            <a href="mailto:{{ user.email }}">{{ user.email }}</a>
+        {%- endif %}
+    </small>
+</h2>
+
+<section class="row">
+    <section class="col-md-12">
+        {% embed "PackagistWebBundle:Web:list.html.twig" with {noLayout: 'true', showAutoUpdateWarning: isActualUser} %}
+            {% block content_title %}
+                <h3 class="font-normal profile-title">{{ user.username }}'s packages</h3>
+            {% endblock %}
+        {% endembed %}
+    </section>
+</section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Web/index.html.twig b/src/Packagist/WebBundle/Resources/views/Web/index.html.twig
new file mode 100644
index 0000000..84b2111
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Web/index.html.twig
@@ -0,0 +1,62 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% block content %}
+<section class="row">
+    <section class="col-lg-6 getting-started">
+        <h2 class="title">Getting Started</h2>
+        <div>
+            <h3>Define Your Dependencies</h3>
+            <p>Put a file named <em>composer.json</em> at the root of your project, containing your project dependencies:</p>
+            <pre><code>{
+    "require": {
+        "vendor/package": "1.3.2",
+        "vendor/package2": "1.*",
+        "vendor/package3": "^2.0.3"
+    }
+}</code></pre>
+
+            <p>For more information about packages versions usage, see the <a href="https://getcomposer.org/doc/01-basic-usage.md#package-versions">composer documentation</a>.</p>
+
+            <h3>Install Composer In Your Project</h3>
+            <p>Run this in your command line:</p>
+            <pre><code>curl -sS https://getcomposer.org/installer | php</code></pre>
+            <p>Or <a href="https://getcomposer.org/composer.phar">download composer.phar</a> into your project root.</p>
+            <p>See the Composer documentation for complete <a href="https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx">installation instructions</a> on various platforms.</p>
+
+            <h3>Install Dependencies</h3>
+            <p>Execute this in your project root.</p>
+            <pre><code>php composer.phar install</code></pre>
+
+            <h3>Autoload Dependencies</h3>
+            <p>If your packages specify <a href="https://getcomposer.org/doc/01-basic-usage.md#autoloading">autoloading information</a>, you can autoload all the dependencies by adding this to your code:</p>
+            <pre><code>require 'vendor/autoload.php';</code></pre>
+            <p><a href="{{ path('browse') }}">Browse</a> the packages we have to find more great libraries you can use in your project.</p>
+        </div>
+    </section>
+
+    <section class="col-lg-6 publishing-packages">
+        <h2 class="title">Publishing Packages</h2>
+        <div>
+            <h3>Define Your Package</h3>
+            <p>Put a file named <em>composer.json</em> at the root of your package, containing this information:</p>
+            <pre><code>{
+    "name": "your-vendor-name/package-name",
+    "description": "A short description of what your package does",
+    "require": {
+        "php": ">=5.3.0",
+        "another-vendor/package": "1.*"
+    }
+}</code></pre>
+            <p>This is the strictly minimal information you have to give.</p>
+            <p>For more details about package naming and the fields you can use to document your package better, see the <a href="{{ path('about') }}">about</a> page.</p>
+
+            <h3>Commit The File</h3>
+            <p>You surely don't need help with that.</p>
+
+            <h3>Publish It</h3>
+            <p><a href="{{ path('hwi_oauth_connect') }}">Login</a> or <a href="{{ path('fos_user_registration_register') }}">register</a> on this site, then hit the <a href="{{ path('submit') }}">submit</a> button in the menu.</p>
+            <p>Once you entered your public repository URL in there, your package will be automatically crawled periodically. You just have to make sure you keep the composer.json file up to date.</p>
+        </div>
+    </section>
+</section>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Web/list.html.twig b/src/Packagist/WebBundle/Resources/views/Web/list.html.twig
new file mode 100644
index 0000000..56fbc9d
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Web/list.html.twig
@@ -0,0 +1,17 @@
+{% extends noLayout|default(false) ? "::base_nolayout.html.twig" : "PackagistWebBundle::layout.html.twig" %}
+
+{% import "PackagistWebBundle::macros.html.twig" as macros %}
+
+{% block content %}
+    {% block content_title %}<h3 class="title">Packages</h3>{% endblock %}
+
+    {% block list %}
+        {% if packages|length %}
+            {{ macros.listPackages(packages, paginate is not defined or paginate, showAutoUpdateWarning|default(false), meta|default(null)) }}
+        {% else %}
+            <div class="alert alert-danger">
+                <p>No packages found.</p>
+            </div>
+        {% endif %}
+    {% endblock %}
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/Web/search.html.twig b/src/Packagist/WebBundle/Resources/views/Web/search.html.twig
new file mode 100644
index 0000000..0109995
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Web/search.html.twig
@@ -0,0 +1,10 @@
+{% embed "PackagistWebBundle:Web:list.html.twig" %}
+    {% block search_results %}
+    {% endblock %}
+
+    {% block content %}
+        <div class="search-list">
+            {{ block('list') }}
+        </div>
+    {% endblock %}
+{% endembed %}
diff --git a/src/Packagist/WebBundle/Resources/views/Web/searchForm.html.twig b/src/Packagist/WebBundle/Resources/views/Web/searchForm.html.twig
new file mode 100644
index 0000000..6a2c370
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Web/searchForm.html.twig
@@ -0,0 +1,32 @@
+{{ form_start(searchForm, { attr: { id: 'search-form', autocomplete: 'off' } }) }}
+    <div class="{% if orderBys is defined %}sortable{% endif %} row">
+        <div class="hidden">{{ form_errors(searchForm.query) }}</div>
+        <div class="{% if searchForm.vars.value.query is empty %}col-xs-12{% else %}col-xs-8{% endif %} js-search-field-wrapper col-md-9">
+            {{ form_widget(searchForm.query, {'attr': {'autocomplete': 'off', 'placeholder': 'Search packages...', 'tabindex': 1}}) }}
+        </div>
+
+        {% set hasActiveOrderBy = false %}
+        {% spaceless %}
+        <div id="order-bys-wrapper" class="col-xs-4 col-md-3 col-lg-2{% if searchForm.vars.value.query is empty %} hidden{% endif %}">
+            {% for sort, param in orderBys %}
+                <a title="{{ param.title }}" href="{{ param.href }}" class="order-by-group">
+                    <i class="glyphicon {{ param.class }}{% if param.arrowClass is not empty %} active{% endif %}"></i>
+                    {% if param.arrowClass is not empty %}
+                        {% set hasActiveOrderBy = true %}
+                    {% endif %}
+                </a>
+            {% endfor %}
+            {% if hasActiveOrderBy %}
+                <a title="Clear order bys" href="?q={{ searchForm.vars.value.query }}" class="clear active">
+                    <i class="glyphicon glyphicon-remove-circle"></i>
+                </a>
+            {% endif %}
+        </div>
+        {% endspaceless %}
+        <div
+            class="hidden">
+            {{ form_widget(searchForm.orderBys) }}
+        </div>
+        {{ form_rest(searchForm) }}
+    </div>
+{{ form_end(searchForm) }}
diff --git a/src/Packagist/WebBundle/Resources/views/Web/searchSection.html.twig b/src/Packagist/WebBundle/Resources/views/Web/searchSection.html.twig
new file mode 100644
index 0000000..acc380f
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Web/searchSection.html.twig
@@ -0,0 +1,14 @@
+<section class="wrapper wrapper-search">
+    <div class="container {% if showSearchDesc == 'show' %}with-description{% endif %}">
+        {{ render(controller('PackagistWebBundle:Web:searchForm', { req: app.request })) }}
+
+        {%- if showSearchDesc == 'show' %}
+        <div class="row">
+            <div class="col-sm-9 hidden-xs">
+                <img src="{{ asset('bundles/packagistweb/img/logo-small.png') }}" class="logo">
+                <p>Packagist is the main <a href="https://getcomposer.org/">Composer</a> repository. It aggregates public PHP packages installable with Composer.</p>
+            </div>
+        </div>
+        {%- endif %}
+    </div>
+</section>
diff --git a/src/Packagist/WebBundle/Resources/views/Web/stats.html.twig b/src/Packagist/WebBundle/Resources/views/Web/stats.html.twig
new file mode 100644
index 0000000..76e7d61
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/Web/stats.html.twig
@@ -0,0 +1,62 @@
+{% extends "PackagistWebBundle::layout.html.twig" %}
+
+{% set showSearchDesc = 'hide' %}
+
+{% block content %}
+    {% set packageCount = 0 %}
+
+    <h2 class="title">Statistics</h2>
+
+    <section class="row">
+        <div class="col-lg-12">
+            <h3>Packages/versions over time</h3>
+
+            <p class="row">
+                <svg class="chart col-xs-12" width="500" height="200" data-labels="{{ chart.months|join(',') }}" data-scale="1000" data-values="Versions:{{ chart.versions|join(',') }}|Packages:{{ chart.packages|join(',') }}">
+                    Sorry, the graph can't be displayed because your browser doesn't support &lt;svg&gt; html element.
+                </svg>
+            </p>
+            <p>The last data point is for the current month and shows partial data.</p>
+
+            {% if downloadsChart %}
+                <h3>Packages installed in the last 30 days</h3>
+                <p class="row">
+                    <svg class="chart col-xs-12" width="500" height="200" data-labels="{{ downloadsChart.labels|join(',') }}" data-scale="1000" data-values="Installs:{{ downloadsChart.values|join(',') }}">
+                        Sorry, the graph can't be displayed because your browser doesn't support &lt;svg&gt; html element.
+                    </svg>
+                </p>
+            {% endif %}
+            {% if downloadsChartMonthly %}
+                <h3>Packages installed per month</h3>
+                <p class="row">
+                    <svg class="chart col-xs-12" width="500" height="200" data-labels="{{ downloadsChartMonthly.labels|join(',') }}" data-scale="1000000" data-values="Installs:{{ downloadsChartMonthly.values|join(',') }}">
+                        Sorry, the graph can't be displayed because your browser doesn't support &lt;svg&gt; html element.
+                    </svg>
+                </p>
+                <p>The last data point is for the current month and shows partial data.</p>
+            {% endif %}
+
+            <h3>Totals</h3>
+            <dl class="dl-horizontal">
+                <dt class="font-normal">Packages registered</dt>
+                <dd class="font-normal">{{ packages|number_format(0, '.', "&#8201;")|raw }}</dd>
+
+                <dt class="font-normal">Versions available</dt>
+                <dd class="font-normal">{{ versions|number_format(0, '.', "&#8201;")|raw }}</dd>
+
+                <dt class="font-normal">Packages installed<br><small>(since {{ downloadsStartDate }})</small></dt>
+                <dd class="font-normal">{% if downloads == 'N/A' %}{{ downloads }}{% else %}{{ downloads|number_format(0, '.', "&#8201;")|raw }}{% endif %}</dd>
+            </dl>
+        </div>
+    </section>
+{% endblock %}
+
+{% block stylesheets %}
+    <link href="https://cdn.rawgit.com/novus/nvd3/v1.7.1/build/nv.d3.css" rel="stylesheet">
+{% endblock %}
+
+{% block scripts %}
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
+    <script src="https://cdn.rawgit.com/novus/nvd3/v1.7.1/build/nv.d3.min.js"></script>
+    <script src="{{ asset('bundles/packagistweb/js/charts.js') }}"></script>
+{% endblock %}
diff --git a/src/Packagist/WebBundle/Resources/views/forms.html.twig b/src/Packagist/WebBundle/Resources/views/forms.html.twig
new file mode 100644
index 0000000..804ce93
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/forms.html.twig
@@ -0,0 +1,45 @@
+{% extends 'form_div_layout.html.twig' %}
+
+{% block textarea_widget %}
+{% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
+{{ parent() }}
+{% endblock textarea_widget %}
+
+{% block form_widget_simple %}
+{% spaceless %}
+    {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
+    {% set type = type|default('text') %}
+
+    {{ parent() }}
+{% endspaceless %}
+{% endblock form_widget_simple %}
+
+{% block form_row %}
+{% spaceless %}
+    <div class="form-group{% if errors|length > 0 %} has-error{% endif %}">
+        {{ form_label(form) }}
+
+        {{ form_widget(form) }}
+
+        {% for error in errors %}
+            <span class="help-block form-error">
+                {{
+                    error.messagePluralization is null
+                    ? error.messageTemplate|trans(error.messageParameters, 'validators')
+                    : error.messageTemplate|transchoice(error.messagePluralization, error.messageParameters, 'validators')
+                }}
+            </span>
+        {% endfor %}
+    </div>
+{% endspaceless %}
+{% endblock form_row %}
+
+{% block form_errors %}
+{% spaceless %}
+    {% if errors|length > 0 %}
+        {% for error in errors %}
+            <div class="alert alert-danger">{{ error.message }}</div>
+        {% endfor %}
+    {% endif %}
+{% endspaceless %}
+{% endblock form_errors %}
diff --git a/src/Packagist/WebBundle/Resources/views/layout.html.twig b/src/Packagist/WebBundle/Resources/views/layout.html.twig
new file mode 100644
index 0000000..036d431
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/layout.html.twig
@@ -0,0 +1,203 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8" />
+
+        <title>{% block title %}Packagist{% endblock %}</title>
+        <meta name="description" content="{% block description %}The PHP Package Repository{% endblock %}" />
+        <meta name="author" content="Jordi Boggiano" />
+
+        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
+        <link rel="shortcut icon" href="{{ asset('favicon.ico') }}" />
+
+        <link rel="stylesheet" href="{{ asset('libs/bootstrap-3.3.5/css/bootstrap.min.css') }}" />
+        <link rel="stylesheet" href="{{ asset('libs/bootstrap-3.3.5/css/bootstrap-theme.min.css') }}" />
+        <link rel="stylesheet" href="{{ asset('bundles/packagistweb/css/main.css') }}" />
+        <link rel="stylesheet" href="{{ asset('css/humane/jackedup.css') }}" />
+        {% block stylesheets %}{% endblock %}
+
+        {% block head_feeds %}
+        <link rel="alternate" type="application/rss+xml" title="Newly Submitted Packages - Packagist" href="{{ url('feed_packages', {_format: 'rss'}) }}" />
+        <link rel="alternate" type="application/rss+xml" title="New Releases - Packagist" href="{{ url('feed_releases', {_format: 'rss'}) }}" />
+        {% endblock %}
+
+        <link rel="search" type="application/opensearchdescription+xml" href="{{ asset('search.osd') }}" title="Packagist" />
+
+        {% block head_additions %}{% endblock %}
+    </head>
+
+    <body>
+    <section class="wrap">
+        <header class="navbar-wrapper navbar-fixed-top">
+            <nav class="container">
+                <div class="navbar" role="navigation">
+                    <div class="navbar-header">
+                        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
+                            <span class="sr-only">Toggle navigation</span>
+                            <span class="icon-bar"></span>
+                            <span class="icon-bar"></span>
+                            <span class="icon-bar"></span>
+                        </button>
+                        <h1 class="navbar-brand"><a href="{{ path('home') }}">Packagist</a> <em class="hidden-sm hidden-xs">The PHP Package Repository</em></h1>
+                    </div>
+
+                    <div class="collapse navbar-collapse">
+                        <ul class="nav navbar-nav">
+                            <li>
+                                <a href="{{ path('browse') }}">Browse</a>
+                            </li>
+                            <li>
+                                <a href="{{ path('submit') }}">Submit</a>
+                            </li>
+                        {%- if app.user %}
+                            <li class="nav-user">
+                                <section>
+                                    <a href="{{ path('fos_user_profile_show') }}">{{ app.user.username }}</a>
+
+                                    <section class="nav-user-menu">
+                                        {{ knp_menu_render('user_menu', {'allow_safe_labels': true}) }}
+                                    </section>
+                                </section>
+                            </li>
+                        {%- else %}
+                            <li>
+                                <a href="{{ path('fos_user_registration_register') }}">Create account</a>
+                            </li>
+                            <li class="nav-user">
+                                <section class="nav-user-signin">
+                                    <a href="{{ path('hwi_oauth_connect') }}">Sign in</a>
+
+                                    <section class="signin-box">
+                                        <form action="{{ path('login_check') }}" method="POST">
+                                            <div class="input-group">
+                                                <input class="form-control" type="text" id="_username" name="_username" placeholder="{{ 'security.login.username'|trans({}, 'FOSUserBundle') }}">
+                                                <span class="input-group-addon"><span class="icon-user"></span></span>
+                                            </div>
+                                            <div class="input-group">
+                                                <input class="form-control" type="password" id="_password" name="_password" placeholder="{{ 'security.login.password'|trans({}, 'FOSUserBundle') }}">
+                                                <span class="input-group-addon"><span class="icon-lock"></span></span>
+                                            </div>
+
+                                            <div class="checkbox">
+                                                <label for="_remember_me">
+                                                    <input type="checkbox" id="_remember_me" name="_remember_me" value="on" checked="checked" />
+                                                    {{- 'security.login.remember_me'|trans({}, 'FOSUserBundle') }}
+                                                </label>
+                                            </div>
+
+                                            {%- if packagist_host and packagist_host in app.request.headers.get('Referer') %}
+                                                <input type="hidden" name="_target_path" value="{{ app.request.headers.get('Referer') }}" />
+                                            {%- endif %}
+
+                                            <div class="signin-box-buttons">
+                                                <a href="{{ hwi_oauth_login_url('github') }}" class="pull-right btn btn-primary btn-github"><span class="icon-github"></span>Use Github</a>
+                                                <button type="submit" class="btn btn-success" id="_submit" name="_submit">{{ 'security.login.submit'|trans({}, 'FOSUserBundle') }}</button>
+                                            </div>
+                                        </form>
+
+                                        <div class="signin-box-register">
+                                            <a href="{{ path('fos_user_registration_register') }}">No account yet? Create one now!</a>
+                                        </div>
+                                    </section>
+                                </section>
+                            </li>
+                        {%- endif %}
+                        </ul>
+                    </div>
+                </div>
+            </nav>
+        </header>
+
+        {{ include("PackagistWebBundle:Web:searchSection.html.twig", {showSearchDesc: showSearchDesc|default('show')}) }}
+
+        {% block flashMessages %}
+            {%- if app.session.flashbag.peekAll()|length > 0 %}
+            <section class="wrapper wrapper-white">
+                <div class="container flash-container">
+                {%- for type, flashMessages in app.session.flashbag.all() %}
+                    {%- for flashMessage in flashMessages %}
+                        {%- if 'fos_user_' in type %}
+                            <div class="alert alert-{{ type|replace({'fos_user_': ''}) }}">
+                                <p>{{ flashMessage|trans({}, 'FOSUserBundle') }}</p>
+                            </div>
+                        {%- else %}
+                            <div class="alert alert-{{ type }}">
+                                <p>{{ flashMessage|trans }}</p>
+                            </div>
+                        {%- endif %}
+                    {%- endfor %}
+                {%- endfor %}
+                </div>
+            </section>
+            {%- endif %}
+        {% endblock %}
+
+        {% block content_header %}{% endblock %}
+
+        <section class="wrapper">
+            <section class="container content" role="main">
+                {% block search_results %}
+                    <div class="row hidden">
+                        <div class="search-list col-md-12"></div>
+                    </div>
+                {% endblock %}
+
+                {% block content %}{% endblock %}
+            </section>
+        </section>
+    </section>
+
+        <footer class="wrapper-footer">
+            <nav class="container">
+                <div class="row">
+                    <ul class="social col-xs-7 col-sm-4 col-md-2 pull-right">
+                        <li><a href="http://github.com/composer/packagist" title="{{ 'menu.github'|trans }}"><span class="icon-github"></span></a></li>
+                        <li><a href="https://twitter.com/packagist" title="{{ 'menu.twitter'|trans }}"><span class="icon-twitter"></span></a></li>
+                        <li><a href="mailto:contact@packagist.org" title="{{ 'menu.contact'|trans }}"><span class="icon-mail"></span></a></li>
+                    </ul>
+
+                    <ul class="col-xs-4 col-md-2">
+                        <li><a href="{{ path('about') }}">{{ 'menu.about_packagist'|trans }}</a></li>
+                        <li><a href="{{ path('feeds') }}">{{ 'menu.rss_feeds'|trans }}</a></li>
+                    </ul>
+
+                    <div class="clearfix visible-xs-block"></div>
+
+                    <ul class="col-xs-3 col-md-2">
+                        <li><a href="{{ path('stats') }}">{{ 'menu.stats'|trans }}</a></li>
+                        <li><a href="{{ path('browse') }}">{{ 'menu.browse_packages'|trans }}</a></li>
+                    </ul>
+
+                    <div class="clearfix visible-xs-block"></div>
+
+                    <ul class="col-xs-3 col-md-2">
+                        <li><a href="{{ path('api_doc') }}">{{ 'menu.api_doc'|trans }}</a></li>
+                    </ul>
+
+                </div>
+
+                <div class="row">
+                    <p class="toran col-xs-12">Packagist maintenance and hosting is supported by <a href="https://toranproxy.com/">Toran Proxy</a></p>
+                </div>
+            </nav>
+        </footer>
+
+        <script src="{{ asset('libs/jquery-2.1.4.min.js') }}"></script>
+        <script src="{{ asset('libs/humane-3.2.2.min.js') }}"></script>
+        <script src="{{ asset('bundles/packagistweb/js/layout.js') }}"></script>
+        <script src="{{ asset('bundles/packagistweb/js/search.js')}}"></script>
+        <script src="{{ asset('libs/bootstrap-3.3.5/js/bootstrap.min.js')}}"></script>
+
+        {%- if not app.debug and google_analytics.ga_key %}
+        <script>
+            var _gaq=[['_setAccount','{{ google_analytics.ga_key }}'],['_trackPageview']];
+            (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.async=1;
+            g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
+            s.parentNode.insertBefore(g,s)}(document,'script'));
+        </script>
+        {%- endif %}
+
+        {% block scripts %}{% endblock %}
+    </body>
+</html>
diff --git a/src/Packagist/WebBundle/Resources/views/macros.html.twig b/src/Packagist/WebBundle/Resources/views/macros.html.twig
new file mode 100644
index 0000000..000c4f9
--- /dev/null
+++ b/src/Packagist/WebBundle/Resources/views/macros.html.twig
@@ -0,0 +1,73 @@
+{% macro listPackages(packages, paginate, showAutoUpdateWarning, meta) %}
+    <ul class="packages list-unstyled">
+        {% for package in packages %}
+            {% if package.id is numeric %}
+                {% set packageUrl = path('view_package', { 'name' : package.name }) %}
+            {% else %}
+                {% set packageUrl = path('view_providers', { 'name' : package.name }) %}
+            {% endif %}
+            <li data-url="{{ packageUrl }}" class="row">
+                <div class="col-xs-12 package-item">
+                    <div class="row">
+                        <div class="col-sm-9 col-lg-10">
+                            {% if package.language is defined and package.language is not empty %}<p class="pull-right language">{{ package.language }}</p>{% endif %}
+                            <h4 class="font-bold">
+                                <a href="{{ packageUrl }}">{{ package.name }}</a>
+                                {% if package.id is not numeric %}
+                                    <small>(Virtual Package)</small>
+                                {% endif %}
+                                {% if showAutoUpdateWarning and not package.autoUpdated %}
+                                    <small>(Not Auto-Updated)</small>
+                                {% endif %}
+                            </h4>
+                            {% if package.description is defined and package.description %}
+                                <p>{{ package.description }}</p>
+                            {% endif %}
+                            {% if package.abandoned is defined and package.abandoned %}
+                                <p class="abandoned">
+                                    <i class="glyphicon glyphicon-exclamation-sign"></i> Abandoned! {% if package.replacementPackage %}See <a href="{{ '://' in package.replacementPackage ? package.replacementPackage : path('view_package', {name: package.replacementPackage}) }}">{{ package.replacementPackage }}</a>{% endif %}
+                                </p>
+                            {% endif %}
+                        </div>
+                        <div class="col-sm-3 col-lg-2">
+                            {% if meta %}
+                                <p class="metadata">
+                                    <span class="metadata-block"><i class="glyphicon glyphicon-arrow-down"></i> {{ meta.downloads[package.id]|default(0)|number_format(0, '.', '&#8201;')|raw }}</span>
+                                    <span class="metadata-block"><i class="glyphicon glyphicon-star"></i> {{ meta.favers[package.id]|default(0)|number_format(0, '.', '&#8201;')|raw }}</span>
+                                </p>
+                            {% endif %}
+                        </div>
+                    </div>
+                </div>
+            </li>
+        {% endfor %}
+
+        {% if paginate is defined and paginate and packages.haveToPaginate() %}
+            {{ pagerfanta(packages, 'twitter_bootstrap', {'proximity': 2}) }}
+        {% endif %}
+    </ul>
+{% endmacro %}
+
+{% macro listPackagesShort(packages, showVersion, showMoreUrl) %}
+    <ul class="list-unstyled">
+        {% for package in packages %}
+            <li>
+                <a href="{{ path('view_package', {'name' : package.name }) }}">{{ package.name }}{% if showVersion is defined and showVersion %}<strong>{{ package.version }}</strong>{% endif %}</a>
+                {{ package.description|truncate(60) }}
+            </li>
+        {% endfor %}
+        {% if showMoreUrl is defined and showMoreUrl %}
+            <li><a href="{{ path('browse_popular') }}">See more...</a></li>
+        {% endif %}
+    </ul>
+{% endmacro %}
+
+{% macro packageLink(packageName, type) %}
+    {%- if type == 'provide' and (packageName is existing_provider or packageName is existing_package) -%}
+        <a href="{{ path('view_providers', { 'name': packageName }) }}">{{ packageName }}</a>
+    {%- elseif packageName is existing_package -%}
+        <a href="{{ path('view_package', { 'name': packageName }) }}">{{ packageName }}</a>
+    {%- else -%}
+        {{ packageName }}
+    {%- endif -%}
+{% endmacro %}
diff --git a/src/Packagist/WebBundle/Security/ExceptionListener.php b/src/Packagist/WebBundle/Security/ExceptionListener.php
new file mode 100644
index 0000000..f017633
--- /dev/null
+++ b/src/Packagist/WebBundle/Security/ExceptionListener.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Packagist\WebBundle\Security;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\Firewall\ExceptionListener as BaseExceptionListener;
+
+class ExceptionListener extends BaseExceptionListener
+{
+    protected function setTargetPath(Request $request)
+    {
+        // Do not save target path for oauth registration
+        if (preg_match('{^/connect/registration}', $request->getPathInfo())) {
+            return;
+        }
+
+        parent::setTargetPath($request);
+    }
+}
diff --git a/src/Packagist/WebBundle/Security/Provider/UserProvider.php b/src/Packagist/WebBundle/Security/Provider/UserProvider.php
new file mode 100644
index 0000000..d00790a
--- /dev/null
+++ b/src/Packagist/WebBundle/Security/Provider/UserProvider.php
@@ -0,0 +1,120 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Security\Provider;
+
+use FOS\UserBundle\Model\UserManagerInterface;
+use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
+use HWI\Bundle\OAuthBundle\Security\Core\Exception\AccountNotLinkedException;
+use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface;
+use Packagist\WebBundle\Entity\User;
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\User\UserProviderInterface;
+
+class UserProvider implements OAuthAwareUserProviderInterface, UserProviderInterface
+{
+    /**
+     * @var UserManagerInterface
+     */
+    private $userManager;
+
+    /**
+     * @var UserProviderInterface
+     */
+    private $userProvider;
+
+    /**
+     * @param UserManagerInterface  $userManager
+     * @param UserProviderInterface $userProvider
+     */
+    public function __construct(UserManagerInterface $userManager, UserProviderInterface $userProvider)
+    {
+        $this->userManager = $userManager;
+        $this->userProvider = $userProvider;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function connect($user, UserResponseInterface $response)
+    {
+        $username = $response->getUsername();
+
+        /** @var User $previousUser */
+        $previousUser = $this->userManager->findUserBy(array('githubId' => $username));
+
+        /** @var User $user */
+        $user->setGithubId($username);
+        $user->setGithubToken($response->getAccessToken());
+
+        // The account is already connected. Do nothing
+        if ($previousUser === $user) {
+            return;
+        }
+
+        // 'disconnect' a previous account
+        if (null !== $previousUser) {
+            $previousUser->setGithubId(null);
+            $previousUser->setGithubToken(null);
+            $this->userManager->updateUser($previousUser);
+        }
+
+        $this->userManager->updateUser($user);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function loadUserByOAuthUserResponse(UserResponseInterface $response)
+    {
+        $username = $response->getUsername();
+        /** @var User $user */
+        $user = $this->userManager->findUserBy(array('githubId' => $username));
+
+        if (!$user) {
+            throw new AccountNotLinkedException(sprintf('No user with github username "%s" was found.', $username));
+        }
+
+        if ($user->getGithubToken() !== $response->getAccessToken()) {
+            $user->setGithubToken($response->getAccessToken());
+            $this->userManager->updateUser($user);
+        }
+
+        return $user;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function loadUserByUsername($usernameOrEmail)
+    {
+        $user = $this->userProvider->loadUserByUsername($usernameOrEmail);
+
+        return $user;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function refreshUser(UserInterface $user)
+    {
+        return $this->userProvider->refreshUser($user);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function supportsClass($class)
+    {
+        return $this->userProvider->supportsClass($class);
+    }
+}
diff --git a/src/Packagist/WebBundle/Tests/Controller/AboutControllerTest.php b/src/Packagist/WebBundle/Tests/Controller/AboutControllerTest.php
new file mode 100644
index 0000000..642f871
--- /dev/null
+++ b/src/Packagist/WebBundle/Tests/Controller/AboutControllerTest.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Packagist\WebBundle\Tests\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+class AboutControllerTest extends WebTestCase
+{
+    public function testPackagist()
+    {
+        $client = self::createClient();
+
+        $crawler = $client->request('GET', '/about');
+        $this->assertEquals('What is Packagist?', $crawler->filter('h2.title')->first()->text());
+    }
+}
diff --git a/src/Packagist/WebBundle/Tests/Controller/ApiControllerTest.php b/src/Packagist/WebBundle/Tests/Controller/ApiControllerTest.php
new file mode 100644
index 0000000..bf867da
--- /dev/null
+++ b/src/Packagist/WebBundle/Tests/Controller/ApiControllerTest.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Packagist\WebBundle\Tests\Controller;
+
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\User;
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+class ApiControllerTest extends WebTestCase
+{
+    public function testGithubFailsCorrectly()
+    {
+        $client = self::createClient();
+
+        $client->request('GET', '/api/github');
+        $this->assertEquals(405, $client->getResponse()->getStatusCode(), 'GET method should not be allowed for GitHub Post-Receive URL');
+
+        $payload = json_encode(array('repository' => array('url' => 'git://github.com/composer/composer',)));
+        $client->request('POST', '/api/github?username=INVALID_USER&apiToken=INVALID_TOKEN', array('payload' => $payload,));
+        $this->assertEquals(403, $client->getResponse()->getStatusCode(), 'POST method should return 403 "Forbidden" if invalid username and API Token are sent');
+    }
+
+    /**
+     * @dataProvider githubApiProvider
+     */
+    public function testGithubApi($url)
+    {
+        $client = self::createClient();
+
+        $package = new Package;
+        $package->setRepository($url);
+
+        $user = new User;
+        $user->addPackages($package);
+
+        $repo = $this->getMockBuilder('Packagist\WebBundle\Entity\UserRepository')->disableOriginalConstructor()->getMock();
+        $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')->disableOriginalConstructor()->getMock();
+        $updater = $this->getMockBuilder('Packagist\WebBundle\Package\Updater')->disableOriginalConstructor()->getMock();
+
+        $repo->expects($this->once())
+            ->method('findOneBy')
+            ->with($this->equalTo(array('username' => 'test', 'apiToken' => 'token')))
+            ->will($this->returnValue($user));
+
+        static::$kernel->getContainer()->set('packagist.user_repository', $repo);
+        static::$kernel->getContainer()->set('doctrine.orm.entity_manager', $em);
+        static::$kernel->getContainer()->set('packagist.package_updater', $updater);
+
+        $payload = json_encode(array('repository' => array('url' => 'git://github.com/composer/composer')));
+        $client->request('POST', '/api/github?username=test&apiToken=token', array('payload' => $payload));
+        $this->assertEquals(202, $client->getResponse()->getStatusCode());
+    }
+
+    public function githubApiProvider()
+    {
+        return array(
+            array('https://github.com/composer/composer.git'),
+            array('http://github.com/composer/composer.git'),
+            array('http://github.com/composer/composer'),
+            array('git@github.com:composer/composer.git'),
+        );
+    }
+
+    /**
+     * @depends testGithubFailsCorrectly
+     * @dataProvider urlProvider
+     */
+    public function testUrlDetection($endpoint, $url, $expectedOK)
+    {
+        $client = self::createClient();
+
+        if ($endpoint == 'bitbucket') {
+            $canonUrl = substr($url, 0, 1);
+            $absUrl = substr($url, 1);
+            $payload = json_encode(array('canon_url' => $canonUrl, 'repository' => array('absolute_url' => $absUrl)));
+        } else {
+            $payload = json_encode(array('repository' => array('url' => $url)));
+        }
+
+        $client->request('POST', '/api/'.$endpoint.'?username=INVALID_USER&apiToken=INVALID_TOKEN', array('payload' => $payload));
+
+        $status = $client->getResponse()->getStatusCode();
+
+        if (!$expectedOK) {
+            $this->assertEquals(406, $status, 'POST method should return 406 "Not Acceptable" if an unknown URL was sent');
+        } else {
+            $this->assertEquals(403, $status, 'POST method should return 403 "Forbidden" for a valid URL with bad credentials.');
+        }
+    }
+
+    public function urlProvider()
+    {
+        return array(
+            // valid github URLs
+            array('github', 'github.com/user/repo', true),
+            array('github', 'github.com/user/repo.git', true),
+            array('github', 'http://github.com/user/repo', true),
+            array('github', 'https://github.com/user/repo', true),
+            array('github', 'https://github.com/user/repo.git', true),
+            array('github', 'git://github.com/user/repo', true),
+            array('github', 'git://github.com/User/Repo.git', true),
+            array('github', 'git@github.com:user/repo.git', true),
+            array('github', 'git@github.com:user/repo', true),
+            array('github', 'https://github.com/user/repo/', true),
+
+            // valid bitbucket URLs
+            array('bitbucket', 'bitbucket.org/user/repo', true),
+            array('bitbucket', 'http://bitbucket.org/user/repo', true),
+            array('bitbucket', 'https://bitbucket.org/user/repo', true),
+
+            // valid others
+            array('update-package', 'https://ghe.example.org/user/repository', true),
+            array('update-package', 'https://gitlab.org/user/repository', true),
+            array('update-package', 'ssh://git@stash.xxxxx.com/uuuuu/qqqqq.git', true),
+            array('update-package', 'ssh://git@stash.xxxxx.com:2222/uuuuu/qqqqq.git', true),
+
+            // invalid URLs
+            array('github', 'php://github.com/user/repository', false),
+            array('github', 'javascript://github.com/user/repository', false),
+            array('github', 'http://', false),
+            array('github', 'https://github.com/user/', false),
+            array('github', 'https://github.com/user', false),
+            array('github', 'https://github.com/', false),
+            array('github', 'https://github.com', false),
+            array('update-package', 'ssh://git@stash.zzzzz.com/kkkkk.git', false),
+            array('update-package', 'ssh://ghe.example.org/user/jjjjj.git', false),
+        );
+    }
+}
diff --git a/src/Packagist/WebBundle/Tests/Controller/FeedControllerTest.php b/src/Packagist/WebBundle/Tests/Controller/FeedControllerTest.php
new file mode 100644
index 0000000..76e6969
--- /dev/null
+++ b/src/Packagist/WebBundle/Tests/Controller/FeedControllerTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Packagist\WebBundle\Tests\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+class FeedControllerTest extends WebTestCase
+{
+    /**
+     * @param string $feed
+     * @param string $format
+     * @param string|null $vendor
+     *
+     * @dataProvider provideForFeed
+     */
+    public function testFeedAction($feed, $format, $vendor = null)
+    {
+        $client = self::createClient();
+
+        $url = $client->getContainer()->get('router')->generate($feed, array('_format' => $format, 'vendor' => $vendor));
+
+        $crawler = $client->request('GET', $url);
+
+        $this->assertEquals(200, $client->getResponse()->getStatusCode(), $client->getResponse()->getContent());
+        $this->assertContains($format, $client->getResponse()->getContent());
+
+        if ($vendor !== null) {
+            $this->assertContains($vendor, $client->getResponse()->getContent());
+        }
+
+    }
+
+
+    public function provideForFeed()
+    {
+        return array(
+            array('feed_packages', 'rss'),
+            array('feed_packages', 'atom'),
+            array('feed_releases', 'rss'),
+            array('feed_releases', 'atom'),
+            array('feed_vendor', 'rss', 'symfony'),
+            array('feed_vendor', 'atom', 'symfony'),
+        );
+    }
+
+}
diff --git a/src/Packagist/WebBundle/Tests/Controller/WebControllerTest.php b/src/Packagist/WebBundle/Tests/Controller/WebControllerTest.php
new file mode 100644
index 0000000..6a7f46b
--- /dev/null
+++ b/src/Packagist/WebBundle/Tests/Controller/WebControllerTest.php
@@ -0,0 +1,467 @@
+<?php
+
+namespace Packagist\WebBundle\Tests\Controller;
+
+use Exception;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Model\DownloadManager;
+use Packagist\WebBundle\Model\FavoriteManager;
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class WebControllerTest extends WebTestCase
+{
+    public function testHomepage()
+    {
+        $client = self::createClient();
+
+        $crawler = $client->request('GET', '/');
+        $this->assertEquals('Getting Started', $crawler->filter('.getting-started h2')->text());
+    }
+
+    public function testPackages()
+    {
+        $client = self::createClient();
+
+        $this->initializePackages($client->getContainer());
+
+        //we expect at least one package
+        $crawler = $client->request('GET', '/packages/');
+        $this->assertTrue($crawler->filter('.packages li')->count() > 0);
+    }
+
+    public function testPackage()
+    {
+        $client = self::createClient();
+
+        $this->initializePackages($client->getContainer());
+
+        //we expect package to be clickable and showing at least 'package' div
+        $crawler = $client->request('GET', '/packages/');
+        $link = $crawler->filter('.packages li a')->first()->attr('href');
+
+        $crawler = $client->request('GET', $link);
+        $this->assertTrue($crawler->filter('.package')->count() > 0);
+    }
+
+    /**
+     * @covers ::nothing
+     */
+    public function testSearchNoOrderBysAction()
+    {
+        $json = $this->commonTestSearchActionOrderBysDownloads();
+
+        $this->assertSame(
+            $this->getJsonResults(
+                array(
+                    $this->getJsonResult('twig/twig', 25, 0),
+                    $this->getJsonResult('composer/packagist', 12, 0),
+                    $this->getJsonResult('symfony/symfony', 42, 0),
+                )
+            ),
+            $json
+        );
+    }
+
+    /**
+     * @covers ::nothing
+     */
+    public function testSearchOrderByDownloadsAscAction()
+    {
+        $json = $this->commonTestSearchActionOrderBysDownloads(
+            array(
+                array(
+                    'sort' => 'downloads',
+                    'order' => 'asc',
+                ),
+            )
+        );
+
+        $this->assertSame(
+            $this->getJsonResults(
+                array(
+                    $this->getJsonResult('composer/packagist', 12, 0),
+                    $this->getJsonResult('twig/twig', 25, 0),
+                    $this->getJsonResult('symfony/symfony', 42, 0),
+                )
+            ),
+            $json
+        );
+    }
+
+    /**
+     * @covers ::nothing
+     */
+    public function testSearchOrderByDownloadsDescAction()
+    {
+        $json = $this->commonTestSearchActionOrderBysDownloads(
+            array(
+                array(
+                    'sort' => 'downloads',
+                    'order' => 'desc',
+                ),
+            )
+        );
+
+        $this->assertSame(
+            $this->getJsonResults(
+                array(
+                    $this->getJsonResult('symfony/symfony', 42, 0),
+                    $this->getJsonResult('twig/twig', 25, 0),
+                    $this->getJsonResult('composer/packagist', 12, 0),
+                )
+            ),
+            $json
+        );
+    }
+
+    /**
+     * @covers ::nothing
+     */
+    public function testSearchOrderByFaversAscAction()
+    {
+        $json = $this->commonTestSearchActionOrderBysFavers(
+            array(
+                array(
+                    'sort' => 'favers',
+                    'order' => 'asc',
+                ),
+            )
+        );
+
+        $this->assertSame(
+            $this->getJsonResults(
+                array(
+                    $this->getJsonResult('composer/packagist', 0, 1),
+                    $this->getJsonResult('twig/twig', 0, 2),
+                    $this->getJsonResult('symfony/symfony', 0, 3),
+                )
+            ),
+            $json
+        );
+    }
+
+    /**
+     * @covers ::nothing
+     */
+    public function testSearchOrderByFaversDescAction()
+    {
+        $json = $this->commonTestSearchActionOrderBysFavers(
+            array(
+                array(
+                    'sort' => 'favers',
+                    'order' => 'desc',
+                ),
+            )
+        );
+
+        $this->assertSame(
+            $this->getJsonResults(
+                array(
+                    $this->getJsonResult('symfony/symfony', 0, 3),
+                    $this->getJsonResult('twig/twig', 0, 2),
+                    $this->getJsonResult('composer/packagist', 0, 1),
+                )
+            ),
+            $json
+        );
+    }
+
+    /**
+     * @covers ::nothing
+     */
+    public function testSearchOrderBysCombinationAction()
+    {
+        $userMock = $this->getMock('Packagist\WebBundle\Entity\User');
+        $userMock1 = $this->getMock('Packagist\WebBundle\Entity\User');
+        $userMock2 = $this->getMock('Packagist\WebBundle\Entity\User');
+
+        $userMock->method('getId')->will($this->returnValue(1));
+        $userMock1->method('getId')->will($this->returnValue(2));
+        $userMock2->method('getId')->will($this->returnValue(3));
+
+        $json = $this->commonTestSearchActionOrderBysAction(
+            function (
+                ContainerInterface $container,
+                Package $twigPackage,
+                Package $packagistPackage,
+                Package $symfonyPackage
+            ) use (
+                $userMock,
+                $userMock1,
+                $userMock2
+            ) {
+                $downloadManager = $container->get('packagist.download_manager');
+
+                /* @var $downloadManager DownloadManager */
+
+                for ($i = 0; $i < 25; $i += 1) {
+                    $downloadManager->addDownload($twigPackage->getId(), 25);
+                }
+                for ($i = 0; $i < 12; $i += 1) {
+                    $downloadManager->addDownload($packagistPackage->getId(), 12);
+                }
+                for ($i = 0; $i < 25; $i += 1) {
+                    $downloadManager->addDownload($symfonyPackage->getId(), 42);
+                }
+
+                $favoriteManager = $container->get('packagist.favorite_manager');
+
+                /* @var $favoriteManager FavoriteManager */
+
+                $favoriteManager->markFavorite($userMock, $packagistPackage);
+
+                $favoriteManager->markFavorite($userMock, $symfonyPackage);
+                $favoriteManager->markFavorite($userMock1, $symfonyPackage);
+                $favoriteManager->markFavorite($userMock2, $symfonyPackage);
+            },
+            array(
+                array(
+                    'sort' => 'downloads',
+                    'order' => 'desc',
+                ),
+                array(
+                    'sort' => 'favers',
+                    'order' => 'desc',
+                ),
+            )
+        );
+
+        $this->assertSame(
+            $this->getJsonResults(
+                array(
+                    $this->getJsonResult('symfony/symfony', 25, 3),
+                    $this->getJsonResult('twig/twig', 25, 0),
+                    $this->getJsonResult('composer/packagist', 12, 1),
+                )
+            ),
+            $json
+        );
+    }
+
+    /**
+     * @param callable $onBeforeIndex
+     * @param array $orderBys
+     *
+     * @return array
+     */
+    protected function commonTestSearchActionOrderBysAction(
+        callable $onBeforeIndex,
+        array $orderBys = array()
+    ) {
+        $client = self::createClient();
+
+        $container = $client->getContainer();
+
+        $kernelRootDir = $container->getParameter('kernel.root_dir');
+
+        $this->executeCommand('php '.$kernelRootDir . '/console doctrine:database:drop --env=test --force', false);
+        $this->executeCommand('php '.$kernelRootDir . '/console doctrine:database:create --env=test');
+        $this->executeCommand('php '.$kernelRootDir . '/console doctrine:schema:create --env=test');
+        $this->executeCommand('php '.$kernelRootDir . '/console redis:flushall --env=test -n');
+
+        $lock = $container->getParameter('kernel.cache_dir').'/composer-indexer.lock';
+
+        $this->executeCommand('rm -f ' . $lock);
+
+        list($twigPackage, $packagistPackage, $symfonyPackage) = $this->initializePackages($container);
+
+        if (!empty($orderBys)) {
+            $orderBysQryStrPart = '&' . http_build_query(
+                array(
+                    'orderBys' => $orderBys
+                )
+            );
+        } else {
+            $orderBysQryStrPart = '';
+        }
+
+        $onBeforeIndex($container, $twigPackage, $packagistPackage, $symfonyPackage);
+
+        $this->executeCommand('php '.$kernelRootDir . '/console packagist:index --env=test --force');
+
+        $client->request('GET', '/search.json?q=' . $orderBysQryStrPart);
+
+        $response = $client->getResponse();
+
+        $content = $client->getResponse()->getContent();
+
+        $this->assertSame(200, $response->getStatusCode(), $content);
+
+        return json_decode($content, true);
+    }
+
+    protected function initializePackages(ContainerInterface $container)
+    {
+        $kernelRootDir = $container->getParameter('kernel.root_dir');
+
+        $this->executeCommand('php '.$kernelRootDir . '/console doctrine:database:drop --env=test --force', false);
+        $this->executeCommand('php '.$kernelRootDir . '/console doctrine:database:create --env=test');
+        $this->executeCommand('php '.$kernelRootDir . '/console doctrine:schema:create --env=test');
+        $this->executeCommand('php '.$kernelRootDir . '/console redis:flushall --env=test -n');
+
+        $em = $container->get('doctrine')->getManager();
+
+        $twigPackage = new Package();
+
+        $twigPackage->setName('twig/twig');
+        $twigPackage->setRepository('https://github.com/twig/twig');
+
+        $packagistPackage = new Package();
+
+        $packagistPackage->setName('composer/packagist');
+        $packagistPackage->setRepository('https://github.com/composer/packagist');
+
+        $symfonyPackage = new Package();
+
+        $symfonyPackage->setName('symfony/symfony');
+        $symfonyPackage->setRepository('https://github.com/symfony/symfony');
+
+        $em->persist($twigPackage);
+        $em->persist($packagistPackage);
+        $em->persist($symfonyPackage);
+
+        $em->flush();
+
+        return [$twigPackage, $packagistPackage, $symfonyPackage];
+    }
+
+    /**
+     * @param array $orderBys
+     *
+     * @return array
+     */
+    protected function commonTestSearchActionOrderBysDownloads(
+        array $orderBys = array()
+    ) {
+        return $this->commonTestSearchActionOrderBysAction(
+            function (
+                ContainerInterface $container,
+                Package $twigPackage,
+                Package $packagistPackage,
+                Package $symfonyPackage
+            ) {
+                $downloadManager = $container->get('packagist.download_manager');
+
+                /* @var $downloadManager DownloadManager */
+
+                for ($i = 0; $i < 25; $i += 1) {
+                    $downloadManager->addDownload($twigPackage->getId(), 25);
+                }
+                for ($i = 0; $i < 12; $i += 1) {
+                    $downloadManager->addDownload($packagistPackage->getId(), 12);
+                }
+                for ($i = 0; $i < 42; $i += 1) {
+                    $downloadManager->addDownload($symfonyPackage->getId(), 42);
+                }
+            },
+            $orderBys
+        );
+    }
+
+    /**
+     * @param array $orderBys
+     *
+     * @return array
+     */
+    protected function commonTestSearchActionOrderBysFavers(
+        array $orderBys = array()
+    ) {
+        $userMock = $this->getMock('Packagist\WebBundle\Entity\User');
+        $userMock1 = $this->getMock('Packagist\WebBundle\Entity\User');
+        $userMock2 = $this->getMock('Packagist\WebBundle\Entity\User');
+
+        $userMock->method('getId')->will($this->returnValue(1));
+        $userMock1->method('getId')->will($this->returnValue(2));
+        $userMock2->method('getId')->will($this->returnValue(3));
+
+        return $this->commonTestSearchActionOrderBysAction(
+            function (
+                ContainerInterface $container,
+                Package $twigPackage,
+                Package $packagistPackage,
+                Package $symfonyPackage
+            ) use (
+                $userMock,
+                $userMock1,
+                $userMock2
+            ) {
+                $favoriteManager = $container->get('packagist.favorite_manager');
+
+                /* @var $favoriteManager FavoriteManager */
+
+                $favoriteManager->markFavorite($userMock, $twigPackage);
+                $favoriteManager->markFavorite($userMock1, $twigPackage);
+
+                $favoriteManager->markFavorite($userMock, $packagistPackage);
+
+                $favoriteManager->markFavorite($userMock, $symfonyPackage);
+                $favoriteManager->markFavorite($userMock1, $symfonyPackage);
+                $favoriteManager->markFavorite($userMock2, $symfonyPackage);
+            },
+            $orderBys
+        );
+    }
+
+    /**
+     * Executes a given command.
+     *
+     * @param string $command a command to execute
+     * @param bool $errorHandling
+     *
+     * @throws Exception when the return code is not 0.
+     */
+    protected function executeCommand(
+        $command,
+        $errorHandling = true
+    ) {
+        $output = array();
+
+        $returnCode = null;;
+
+        exec($command, $output, $returnCode);
+
+        if ($errorHandling && $returnCode !== 0) {
+            throw new Exception(
+                sprintf(
+                    'Error executing command "%s", return code was "%s".',
+                    $command,
+                    $returnCode
+                )
+            );
+        }
+    }
+
+    /**
+     * @param string $package
+     * @param int $downloads
+     * @param int $favers
+     *
+     * @return array
+     */
+    protected function getJsonResult($package, $downloads, $favers)
+    {
+        return array(
+            'name' => $package,
+            'description' => '',
+            'url' => 'http://localhost/packages/' . $package,
+            'repository' => 'https://github.com/' . $package,
+            'downloads' => $downloads,
+            'favers' => $favers,
+        );
+    }
+
+    /**
+     * @param array $results
+     *
+     * @return array
+     */
+    protected function getJsonResults(
+        array $results
+    ) {
+        return array(
+            'results' => $results,
+            'total' => count($results)
+        );
+    }
+}
diff --git a/src/Packagist/WebBundle/Tests/Package/SymlinkDumperTest.php b/src/Packagist/WebBundle/Tests/Package/SymlinkDumperTest.php
new file mode 100644
index 0000000..92bad88
--- /dev/null
+++ b/src/Packagist/WebBundle/Tests/Package/SymlinkDumperTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Packagist\WebBundle\Tests\Package;
+
+class SymlinkDumperTest extends \PHPUnit_Framework_TestCase
+{
+    private $mockDumper;
+
+    public function setUp()
+    {
+        $this->mockDumper = $this->getMockBuilder('Packagist\WebBundle\Package\SymlinkDumper')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+
+    public function tearDown()
+    {
+        $this->mockDumper = null;
+    }
+
+    /**
+     * @dataProvider getTestGetTargetListingBlocks
+     */
+    public function testGetTargetListingBlocks($now, array $expected)
+    {
+
+        $blocks = self::invoke($this->mockDumper, 'getTargetListingBlocks', $now);
+
+        $blocks = array_map(function($timestamp) { return date('Y-m-d', $timestamp); }, $blocks);
+
+        $this->assertEquals($expected, $blocks);
+    }
+
+    public function getTestGetTargetListingBlocks()
+    {
+        return array(
+            array(
+                strtotime('2014-12-31'),
+                array(
+                    'latest'  => '2014-12-22',
+                    '2014-10' => '2014-10-01',
+                    '2014-07' => '2014-07-01',
+                    '2014-04' => '2014-04-01',
+                    '2014-01' => '2014-01-01',
+                    '2013'    => '2013-01-01',
+                ),
+            ),
+            array(
+                strtotime('2015-01-01'),
+                array(
+                    'latest'  => '2014-12-22',
+                    '2015-01' => '2015-01-01',
+                    '2014-10' => '2014-10-01',
+                    '2014-07' => '2014-07-01',
+                    '2014-04' => '2014-04-01',
+                    '2014'    => '2014-01-01',
+                    '2013'    => '2013-01-01',
+                ),
+            ),
+            array(
+                strtotime('2015-05-31'),
+                array(
+                    'latest'  => '2015-05-25',
+                    '2015-04' => '2015-04-01',
+                    '2015-01' => '2015-01-01',
+                    '2014-10' => '2014-10-01',
+                    '2014-07' => '2014-07-01',
+                    '2014'    => '2014-01-01',
+                    '2013'    => '2013-01-01',
+                ),
+            ),
+        );
+    }
+
+    private static function invoke($object, $method)
+    {
+        $refl = new \ReflectionMethod($object, $method);
+        $refl->setAccessible(true);
+
+        $args = func_get_args();
+        array_shift($args); // object
+        array_shift($args); // method
+
+        return $refl->invokeArgs($object, $args);
+    }
+}
diff --git a/src/Packagist/WebBundle/Twig/PackagistExtension.php b/src/Packagist/WebBundle/Twig/PackagistExtension.php
new file mode 100644
index 0000000..899492a
--- /dev/null
+++ b/src/Packagist/WebBundle/Twig/PackagistExtension.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Packagist\WebBundle\Twig;
+
+use Symfony\Bridge\Doctrine\RegistryInterface;
+
+class PackagistExtension extends \Twig_Extension
+{
+    /**
+     * @var \Symfony\Bridge\Doctrine\RegistryInterface
+     */
+    private $doctrine;
+
+    public function __construct(RegistryInterface $doctrine)
+    {
+        $this->doctrine = $doctrine;
+    }
+
+    public function getTests()
+    {
+        return array(
+            new \Twig_SimpleTest('existing_package', [$this, 'packageExistsTest']),
+            new \Twig_SimpleTest('existing_provider', [$this, 'providerExistsTest']),
+            new \Twig_SimpleTest('numeric', [$this, 'numericTest']),
+        );
+    }
+
+    public function getFilters()
+    {
+        return array(
+            new \Twig_SimpleFilter('prettify_source_reference', [$this, 'prettifySourceReference']),
+            new \Twig_SimpleFilter('gravatar_hash', [$this, 'generateGravatarHash'])
+        );
+    }
+
+    public function getName()
+    {
+        return 'packagist';
+    }
+
+    public function numericTest($val)
+    {
+        return ctype_digit((string) $val);
+    }
+
+    public function packageExistsTest($package)
+    {
+        if (!preg_match('/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/', $package)) {
+            return false;
+        }
+
+        $repo = $this->doctrine->getRepository('PackagistWebBundle:Package');
+
+        return $repo->packageExists($package);
+    }
+
+    public function providerExistsTest($package)
+    {
+        $repo = $this->doctrine->getRepository('PackagistWebBundle:Package');
+
+        return $repo->packageIsProvided($package);
+    }
+
+    public function prettifySourceReference($sourceReference)
+    {
+        if (preg_match('/^[a-f0-9]{40}$/', $sourceReference)) {
+            return substr($sourceReference, 0, 7);
+        }
+
+        return $sourceReference;
+    }
+
+    public function generateGravatarHash($email)
+    {
+        return md5(strtolower($email));
+    }
+}
diff --git a/src/Packagist/WebBundle/Util/UserManipulator.php b/src/Packagist/WebBundle/Util/UserManipulator.php
new file mode 100644
index 0000000..ca1cf73
--- /dev/null
+++ b/src/Packagist/WebBundle/Util/UserManipulator.php
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Util;
+
+use FOS\UserBundle\Model\UserManagerInterface;
+use FOS\UserBundle\Util\TokenGeneratorInterface;
+use FOS\UserBundle\Util\UserManipulator as BaseManipulator;
+
+class UserManipulator extends BaseManipulator
+{
+    private $userManager;
+    private $tokenGenerator;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(UserManagerInterface $userManager, TokenGeneratorInterface $tokenGenerator)
+    {
+        $this->userManager = $userManager;
+        $this->tokenGenerator = $tokenGenerator;
+
+        parent::__construct($userManager);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function create($username, $password, $email, $active, $superadmin)
+    {
+        $user = parent::create($username, $password, $email, $active, $superadmin);
+
+        $apiToken = substr($this->tokenGenerator->generateToken(), 0, 20);
+        $user->setApiToken($apiToken);
+
+        $this->userManager->updateUser($user);
+
+        return $user;
+    }
+}
diff --git a/web/.htaccess b/web/.htaccess
new file mode 100644
index 0000000..976f529
--- /dev/null
+++ b/web/.htaccess
@@ -0,0 +1,232 @@
+<IfModule mod_setenvif.c>
+  <IfModule mod_headers.c>
+    BrowserMatch MSIE ie
+    Header set X-UA-Compatible "IE=Edge,chrome=1" env=ie
+  </IfModule>
+</IfModule>
+
+<IfModule mod_headers.c>
+# Because X-UA-Compatible isn't sent to non-IE (to save header bytes),
+#   We need to inform proxies that content changes based on UA
+  Header append Vary User-Agent
+# Cache control is set only if mod_headers is enabled, so that's unncessary to declare
+</IfModule>
+
+# ----------------------------------------------------------------------
+# Webfont access
+# ----------------------------------------------------------------------
+
+# allow access from all domains for webfonts
+# alternatively you could only whitelist
+#   your subdomains like "sub.domain.com"
+
+<FilesMatch "\.(ttf|otf|eot|woff|font.css)$">
+  <IfModule mod_headers.c>
+    Header set Access-Control-Allow-Origin "*"
+  </IfModule>
+</FilesMatch>
+
+# ----------------------------------------------------------------------
+# Proper MIME type for all files
+# ----------------------------------------------------------------------
+
+# audio
+AddType audio/ogg                      oga ogg
+
+# video
+AddType video/ogg                      ogv
+AddType video/mp4                      mp4
+AddType video/webm                     webm
+
+# Proper svg serving. Required for svg webfonts on iPad
+#   twitter.com/FontSquirrel/status/14855840545
+AddType     image/svg+xml              svg svgz
+AddEncoding gzip                       svgz
+
+# webfonts
+AddType application/vnd.ms-fontobject  eot
+AddType font/truetype                  ttf
+AddType font/opentype                  otf
+AddType application/x-font-woff        woff
+
+# assorted types
+AddType image/x-icon                   ico
+AddType image/webp                     webp
+AddType text/cache-manifest            appcache manifest
+AddType text/x-component               htc
+AddType application/x-chrome-extension crx
+AddType application/x-xpinstall        xpi
+AddType application/octet-stream       safariextz
+
+
+##   # ----------------------------------------------------------------------
+##   # Expires headers (for better cache control)
+##   # ----------------------------------------------------------------------
+##
+##   # these are pretty far-future expires headers
+##   # they assume you control versioning with cachebusting query params like
+##   #   <script src="application.js?20100608">
+##   # additionally, consider that outdated proxies may miscache
+##   #   www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
+##
+##   # if you don't use filenames to version, lower the css and js to something like
+##   #   "access plus 1 week" or so
+##
+##   <IfModule mod_expires.c>
+##     ExpiresActive on
+##
+##   # Perhaps better to whitelist expires rules? Perhaps.
+##     ExpiresDefault                          "access plus 1 month"
+##
+##   # cache.appcache needs re-requests in FF 3.6 (thx Remy ~Introducing HTML5)
+##     ExpiresByType text/cache-manifest       "access plus 0 seconds"
+##
+##   # your document html
+##     ExpiresByType text/html                 "access plus 0 seconds"
+##
+##   # data
+##     ExpiresByType text/xml                  "access plus 0 seconds"
+##     ExpiresByType application/xml           "access plus 0 seconds"
+##     ExpiresByType application/json          "access plus 0 seconds"
+##
+##   # rss feed
+##     ExpiresByType application/rss+xml       "access plus 1 hour"
+##
+##   # favicon (cannot be renamed)
+##     ExpiresByType image/x-icon              "access plus 1 week"
+##
+##   # media: images, video, audio
+##     ExpiresByType image/gif                 "access plus 1 month"
+##     ExpiresByType image/png                 "access plus 1 month"
+##     ExpiresByType image/jpg                 "access plus 1 month"
+##     ExpiresByType image/jpeg                "access plus 1 month"
+##     ExpiresByType video/ogg                 "access plus 1 month"
+##     ExpiresByType audio/ogg                 "access plus 1 month"
+##     ExpiresByType video/mp4                 "access plus 1 month"
+##     ExpiresByType video/webm                "access plus 1 month"
+##
+##   # htc files  (css3pie)
+##     ExpiresByType text/x-component          "access plus 1 month"
+##
+##   # webfonts
+##     ExpiresByType font/truetype             "access plus 1 month"
+##     ExpiresByType font/opentype             "access plus 1 month"
+##     ExpiresByType application/x-font-woff   "access plus 1 month"
+##     ExpiresByType image/svg+xml             "access plus 1 month"
+##     ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
+##
+##   # css and javascript
+##     ExpiresByType text/css                  "access plus 2 months"
+##     ExpiresByType application/javascript    "access plus 2 months"
+##     ExpiresByType text/javascript           "access plus 2 months"
+##
+##     <IfModule mod_headers.c>
+##       Header append Cache-Control "public"
+##     </IfModule>
+##
+##   </IfModule>
+
+
+
+# ----------------------------------------------------------------------
+# ETag removal
+# ----------------------------------------------------------------------
+
+# Since we're sending far-future expires, we don't need ETags for
+# static content.
+#   developer.yahoo.com/performance/rules.html#etags
+FileETag None
+
+# ----------------------------------------------------------------------
+# Start rewrite engine
+# ----------------------------------------------------------------------
+
+<IfModule mod_rewrite.c>
+    RewriteEngine On
+    RewriteRule ^bundles/packagistweb/(.*) /bundles/drupalpackagist/$1 [L]
+</IfModule>
+
+# ----------------------------------------------------------------------
+# Suppress or force the "www." at the beginning of URLs
+# ----------------------------------------------------------------------
+
+# The same content should never be available under two different URLs - especially not with and
+# without "www." at the beginning, since this can cause SEO problems (duplicate content).
+# That's why you should choose one of the alternatives and redirect the other one.
+
+# By default option 1 (no "www.") is activated. Remember: Shorter URLs are sexier.
+# no-www.org/faq.php?q=class_b
+
+# If you rather want to use option 2, just comment out all option 1 lines
+# and uncomment option 2.
+# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
+
+# ----------------------------------------------------------------------
+
+# Option 1:
+# Rewrite "www.domain.com -> domain.com"
+
+<IfModule mod_rewrite.c>
+  RewriteCond %{HTTPS} !=on
+  RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+  RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
+</IfModule>
+
+# ----------------------------------------------------------------------
+# Symfony front controller
+# ----------------------------------------------------------------------
+
+<IfModule mod_rewrite.c>
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteRule ^(.*)$ app.php [QSA,L]
+</IfModule>
+
+# ----------------------------------------------------------------------
+# Built-in filename-based cache busting
+# ----------------------------------------------------------------------
+
+# If you're not using the build script to manage your filename version revving,
+# you might want to consider enabling this, which will route requests for
+# /css/all.20110203.css to /res/all.css
+
+# To understand why this is important and a better idea than all.css?v1231,
+# read: github.com/paulirish/html5-boilerplate/wiki/Version-Control-with-Cachebusting
+
+# Uncomment to enable.
+# <IfModule mod_rewrite.c>
+#   RewriteCond %{REQUEST_FILENAME} !-f
+#   RewriteCond %{REQUEST_FILENAME} !-d
+#   RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
+# </IfModule>
+
+# ----------------------------------------------------------------------
+# Prevent SSL cert warnings
+# ----------------------------------------------------------------------
+
+# Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent
+# https://www.domain.com when your cert only allows https://secure.domain.com
+# Uncomment the following lines to use this feature.
+
+# <IfModule mod_rewrite.c>
+#   RewriteCond %{SERVER_PORT} !^443
+#   RewriteRule (.*) https://example-domain-please-change-me.com/$1 [R=301,L]
+# </IfModule>
+
+# ----------------------------------------------------------------------
+# Prevent 404 errors for non-existing redirected folders
+# ----------------------------------------------------------------------
+
+# without -MultiViews, Apache will give a 404 for a rewrite if a folder of the same name does not exist
+#   e.g. /blog/hello : webmasterworld.com/apache/3808792.htm
+
+Options -MultiViews
+
+# ----------------------------------------------------------------------
+# UTF-8 encoding
+# ----------------------------------------------------------------------
+
+# use utf-8 encoding for anything served text/plain or text/html
+AddDefaultCharset utf-8
+
+# force utf-8 for a number of file formats
+AddCharset utf-8 .html .css .js .xml .json .rss
diff --git a/web/app.php b/web/app.php
new file mode 100644
index 0000000..7b9d158
--- /dev/null
+++ b/web/app.php
@@ -0,0 +1,26 @@
+<?php
+
+ini_set('date.timezone', 'UTC');
+
+use Symfony\Component\ClassLoader\ApcClassLoader;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * @var \Symfony\Component\ClassLoader\ClassLoader
+ */
+$loader = require __DIR__.'/../app/autoload.php';
+include_once __DIR__.'/../app/bootstrap.php.cache';
+
+// Use APC for autoloading to improve performance.
+if (function_exists('apc_store')) {
+    $apcLoader = new ApcClassLoader(sha1(__FILE__), $loader);
+    $loader->unregister();
+    $apcLoader->register(true);
+}
+
+$kernel = new AppKernel('prod', false);
+$kernel->loadClassCache();
+$request = Request::createFromGlobals();
+$response = $kernel->handle($request);
+$response->send();
+$kernel->terminate($request, $response);
diff --git a/web/app_dev.php b/web/app_dev.php
new file mode 100644
index 0000000..9a4676f
--- /dev/null
+++ b/web/app_dev.php
@@ -0,0 +1,34 @@
+<?php
+
+ini_set('date.timezone', 'UTC');
+
+use Composer\Autoload\ClassLoader;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Debug\Debug;
+
+// If you don't want to setup permissions the proper way, just uncomment the following PHP line
+// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information
+//umask(0000);
+
+// This check prevents access to debug front controllers that are deployed by accident to production servers.
+// Feel free to remove this, extend it, or make something more sophisticated.
+if (isset($_SERVER['HTTP_CLIENT_IP'])
+    || isset($_SERVER['HTTP_X_FORWARDED_FOR'])
+    || !in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', 'fe80::1', '::1'))
+) {
+    header('HTTP/1.0 403 Forbidden');
+    exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
+}
+
+/**
+ * @var ClassLoader
+ */
+$loader = require_once __DIR__.'/../app/autoload.php';
+Debug::enable();
+
+$kernel = new AppKernel('dev', true);
+$kernel->loadClassCache();
+$request = Request::createFromGlobals();
+$response = $kernel->handle($request);
+$response->send();
+$kernel->terminate($request, $response);
diff --git a/web/apple-touch-icon-precomposed.png b/web/apple-touch-icon-precomposed.png
new file mode 100644
index 0000000..8132e4a
Binary files /dev/null and b/web/apple-touch-icon-precomposed.png differ
diff --git a/web/apple-touch-icon.png b/web/apple-touch-icon.png
new file mode 100644
index 0000000..8132e4a
Binary files /dev/null and b/web/apple-touch-icon.png differ
diff --git a/web/css/humane/jackedup.css b/web/css/humane/jackedup.css
new file mode 100644
index 0000000..65be49b
--- /dev/null
+++ b/web/css/humane/jackedup.css
@@ -0,0 +1,118 @@
+html,
+body {
+  min-height: 100%;
+}
+.humane,
+.humane-jackedup {
+  position: fixed;
+  -moz-transition: all 0.6s ease-in-out;
+  -webkit-transition: all 0.6s ease-in-out;
+  -ms-transition: all 0.6s ease-in-out;
+  -o-transition: all 0.6s ease-in-out;
+  transition: all 0.6s ease-in-out;
+  z-index: 100000;
+  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
+}
+.humane,
+.humane-jackedup {
+  font-family: Helvetica Neue, Helvetica, san-serif;
+  font-size: 18px;
+  letter-spacing: -1px;
+  top: 20px;
+  left: 30%;
+  opacity: 0;
+  width: 40%;
+  max-height: 500px;
+  color: #333;
+  padding: 10px;
+  text-align: center;
+  background-color: #fafafa;
+  -moz-border-radius: 2px;
+  -webkit-border-radius: 2px;
+  border-radius: 2px;
+  text-shadow: 0 1px 1px rgba(255,255,255,0.8);
+  -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.5);
+  -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5);
+  box-shadow: 0 1px 2px rgba(0,0,0,0.5);
+  -moz-transform: translateY(-100px);
+  -webkit-transform: translateY(-100px);
+  -ms-transform: translateY(-100px);
+  -o-transform: translateY(-100px);
+  transform: translateY(-100px);
+  overflow: auto;
+}
+.humane p,
+.humane-jackedup p,
+.humane ul,
+.humane-jackedup ul {
+  margin: 0;
+  padding: 0;
+}
+.humane ul,
+.humane-jackedup ul {
+  list-style: none;
+}
+.humane.humane-jackedup-info,
+.humane-jackedup.humane-jackedup-info {
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADICAYAAAAp8ov1AAAABmJLR0QA/wD/AP+gvaeTAAAAR0lEQVQokWNISfn/n4mBgeE/EwMDAwMqQYQYmdoGlxgjI4rY//+Dx2nUFRsQZ2ALTrQQp8QL1DWeqASC014y7aCx8QwMDAwA1aZBIulmpvwAAAAASUVORK5CYII=');
+  background: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(0,0,0,0.7)), color-stop(1, rgba(0,0,0,0.85))) no-repeat;
+  background: -moz-linear-gradient(top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.85) 100%) no-repeat;
+  background: -webkit-linear-gradient(top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.85) 100%) no-repeat;
+  background: -ms-linear-gradient(top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.85) 100%) no-repeat;
+  background: -o-linear-gradient(top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.85) 100%) no-repeat;
+  background: linear-gradient(top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.85) 100%) no-repeat;
+  background-color: #fff;
+  color: #fff;
+  text-shadow: 0 -1px 1px rgba(0,0,0,0.35);
+}
+.humane.humane-jackedup-success,
+.humane-jackedup.humane-jackedup-success {
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADICAYAAAAp8ov1AAAABmJLR0QA/wD/AP+gvaeTAAAASElEQVQokc2SMQ4AIAgDD9/K/79QVzWaENTownAJbWnA5SqACkA/Aiy59hczrGVC30Q7y57EmNU5NL5zwln50IMsfZMel+UBKtFBQSLWM9wLAAAAAElFTkSuQmCC');
+  background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #62c462), color-stop(1, #57a957)) no-repeat;
+  background: -moz-linear-gradient(top, #62c462 0%, #57a957 100%) no-repeat;
+  background: -webkit-linear-gradient(top, #62c462 0%, #57a957 100%) no-repeat;
+  background: -ms-linear-gradient(top, #62c462 0%, #57a957 100%) no-repeat;
+  background: -o-linear-gradient(top, #62c462 0%, #57a957 100%) no-repeat;
+  background: linear-gradient(top, #62c462 0%, #57a957 100%) no-repeat;
+  background-color: #64ff64;
+  color: #fff;
+  text-shadow: 0 -1px 1px rgba(0,0,0,0.35);
+}
+.humane.humane-jackedup-error,
+.humane-jackedup.humane-jackedup-error {
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADICAIAAACmkByiAAAABmJLR0QA/wD/AP+gvaeTAAAAf0lEQVQokY2TOQ7AIAwER/5mivy/yRc2RQDhA0jhghFYO5bhuS+TZMAoIUMEhhH4loGhfu71cenM3DutWMsaeGKjv3zO5N17KLPJ0+fQD8cpv5uVLPo4vnX0PpXj0nuaaeVzdmw+yXG1O96n2p3kozB757Ni1Z5UPsU9SP8AeAG1kHXE+7RlPAAAAABJRU5ErkJggg==');
+  background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ee5f5b), color-stop(1, #c43c35)) no-repeat;
+  background: -moz-linear-gradient(top, #ee5f5b 0%, #c43c35 100%) no-repeat;
+  background: -webkit-linear-gradient(top, #ee5f5b 0%, #c43c35 100%) no-repeat;
+  background: -ms-linear-gradient(top, #ee5f5b 0%, #c43c35 100%) no-repeat;
+  background: -o-linear-gradient(top, #ee5f5b 0%, #c43c35 100%) no-repeat;
+  background: linear-gradient(top, #ee5f5b 0%, #c43c35 100%) no-repeat;
+  background-color: #ee5f5b;
+  color: #fff;
+  text-shadow: 0 -1px 1px rgba(0,0,0,0.35);
+}
+.humane-animate,
+.humane-jackedup.humane-jackedup-animate {
+  opacity: 1;
+  -moz-transform: translateY(0);
+  -webkit-transform: translateY(0);
+  -ms-transform: translateY(0);
+  -o-transform: translateY(0);
+  transform: translateY(0);
+}
+.humane-js-animate,
+.humane-jackedup.humane-jackedup-js-animate {
+  opacity: 1;
+  -moz-transform: translateY(0);
+  -webkit-transform: translateY(0);
+  -ms-transform: translateY(0);
+  -o-transform: translateY(0);
+  transform: translateY(0);
+}
+
+.humane pre {
+  background: #2d2d32;
+  color: #fff;
+  text-shadow: none;
+  text-align: left;
+}
diff --git a/web/favicon.ico b/web/favicon.ico
new file mode 100644
index 0000000..4d9cd5e
Binary files /dev/null and b/web/favicon.ico differ
diff --git a/web/libs/bootstrap-3.3.5/css/bootstrap-theme.min.css b/web/libs/bootstrap-3.3.5/css/bootstrap-theme.min.css
new file mode 100644
index 0000000..61358b1
--- /dev/null
+++ b/web/libs/bootstrap-3.3.5/css/bootstrap-theme.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
\ No newline at end of file
diff --git a/web/libs/bootstrap-3.3.5/css/bootstrap.min.css b/web/libs/bootstrap-3.3.5/css/bootstrap.min.css
new file mode 100644
index 0000000..d65c66b
--- /dev/null
+++ b/web/libs/bootstrap-3.3.5/css/bootstrap.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000..b93a495
Binary files /dev/null and b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot differ
diff --git a/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.svg b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000..94fb549
--- /dev/null
+++ b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,288 @@
+<?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">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="400" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
+<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xa0;" />
+<glyph unicode="&#xa5;" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
+<glyph unicode="&#x2000;" horiz-adv-x="650" />
+<glyph unicode="&#x2001;" horiz-adv-x="1300" />
+<glyph unicode="&#x2002;" horiz-adv-x="650" />
+<glyph unicode="&#x2003;" horiz-adv-x="1300" />
+<glyph unicode="&#x2004;" horiz-adv-x="433" />
+<glyph unicode="&#x2005;" horiz-adv-x="325" />
+<glyph unicode="&#x2006;" horiz-adv-x="216" />
+<glyph unicode="&#x2007;" horiz-adv-x="216" />
+<glyph unicode="&#x2008;" horiz-adv-x="162" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="325" />
+<glyph unicode="&#x20ac;" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
+<glyph unicode="&#x20bd;" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
+<glyph unicode="&#x2212;" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#x231b;" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#x2601;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
+<glyph unicode="&#x26fa;" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
+<glyph unicode="&#x2709;" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
+<glyph unicode="&#x270f;" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
+<glyph unicode="&#xe001;" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
+<glyph unicode="&#xe002;" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
+<glyph unicode="&#xe003;" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
+<glyph unicode="&#xe005;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
+<glyph unicode="&#xe006;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
+<glyph unicode="&#xe007;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
+<glyph unicode="&#xe008;" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
+<glyph unicode="&#xe009;" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
+<glyph unicode="&#xe010;" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe011;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
+<glyph unicode="&#xe012;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe013;" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
+<glyph unicode="&#xe014;" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
+<glyph unicode="&#xe015;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe016;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe017;" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
+<glyph unicode="&#xe018;" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe019;" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
+<glyph unicode="&#xe020;" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
+<glyph unicode="&#xe021;" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe022;" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
+<glyph unicode="&#xe023;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe024;" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
+<glyph unicode="&#xe025;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe026;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
+<glyph unicode="&#xe027;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
+<glyph unicode="&#xe028;" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
+<glyph unicode="&#xe029;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe030;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
+<glyph unicode="&#xe031;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
+<glyph unicode="&#xe032;" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe033;" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
+<glyph unicode="&#xe034;" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
+<glyph unicode="&#xe035;" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
+<glyph unicode="&#xe036;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
+<glyph unicode="&#xe037;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
+<glyph unicode="&#xe038;" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
+<glyph unicode="&#xe039;" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
+<glyph unicode="&#xe040;" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
+<glyph unicode="&#xe041;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe042;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe043;" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
+<glyph unicode="&#xe044;" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe045;" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
+<glyph unicode="&#xe046;" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
+<glyph unicode="&#xe047;" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
+<glyph unicode="&#xe048;" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
+<glyph unicode="&#xe049;" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
+<glyph unicode="&#xe050;" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
+<glyph unicode="&#xe051;" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
+<glyph unicode="&#xe052;" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe053;" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe054;" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe055;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe056;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe057;" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe058;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe059;" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="&#xe060;" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
+<glyph unicode="&#xe062;" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
+<glyph unicode="&#xe063;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
+<glyph unicode="&#xe064;" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
+<glyph unicode="&#xe065;" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
+<glyph unicode="&#xe066;" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
+<glyph unicode="&#xe067;" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
+<glyph unicode="&#xe068;" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe069;" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe070;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe071;" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
+<glyph unicode="&#xe072;" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
+<glyph unicode="&#xe073;" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe074;" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe075;" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
+<glyph unicode="&#xe076;" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe077;" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe078;" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe079;" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
+<glyph unicode="&#xe080;" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
+<glyph unicode="&#xe081;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe082;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe083;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
+<glyph unicode="&#xe084;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
+<glyph unicode="&#xe085;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe086;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe087;" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
+<glyph unicode="&#xe088;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe089;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe090;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
+<glyph unicode="&#xe091;" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
+<glyph unicode="&#xe092;" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe093;" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
+<glyph unicode="&#xe094;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe095;" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe096;" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
+<glyph unicode="&#xe097;" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
+<glyph unicode="&#xe101;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe102;" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
+<glyph unicode="&#xe103;" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
+<glyph unicode="&#xe104;" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
+<glyph unicode="&#xe105;" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe106;" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe107;" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
+<glyph unicode="&#xe108;" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
+<glyph unicode="&#xe109;" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
+<glyph unicode="&#xe110;" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
+<glyph unicode="&#xe111;" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="&#xe112;" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
+<glyph unicode="&#xe113;" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
+<glyph unicode="&#xe114;" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
+<glyph unicode="&#xe115;" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe116;" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
+<glyph unicode="&#xe117;" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="&#xe118;" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="&#xe119;" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe120;" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
+<glyph unicode="&#xe121;" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
+<glyph unicode="&#xe122;" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
+<glyph unicode="&#xe123;" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
+<glyph unicode="&#xe124;" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
+<glyph unicode="&#xe125;" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe126;" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
+<glyph unicode="&#xe127;" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe128;" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe129;" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe130;" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
+<glyph unicode="&#xe131;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
+<glyph unicode="&#xe132;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
+<glyph unicode="&#xe133;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
+<glyph unicode="&#xe134;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe135;" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
+<glyph unicode="&#xe136;" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
+<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
+<glyph unicode="&#xe138;" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
+<glyph unicode="&#xe139;" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="&#xe140;" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
+<glyph unicode="&#xe141;" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
+<glyph unicode="&#xe142;" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
+<glyph unicode="&#xe143;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
+<glyph unicode="&#xe144;" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
+<glyph unicode="&#xe145;" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
+<glyph unicode="&#xe146;" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
+<glyph unicode="&#xe148;" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
+<glyph unicode="&#xe149;" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
+<glyph unicode="&#xe150;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe151;" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
+<glyph unicode="&#xe152;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
+<glyph unicode="&#xe153;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
+<glyph unicode="&#xe154;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
+<glyph unicode="&#xe155;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="&#xe156;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="&#xe157;" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="&#xe158;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe159;" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
+<glyph unicode="&#xe160;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
+<glyph unicode="&#xe161;" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe162;" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
+<glyph unicode="&#xe163;" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe164;" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
+<glyph unicode="&#xe165;" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="&#xe166;" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe167;" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe168;" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
+<glyph unicode="&#xe169;" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe170;" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe171;" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
+<glyph unicode="&#xe172;" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
+<glyph unicode="&#xe173;" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe174;" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
+<glyph unicode="&#xe175;" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe176;" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe177;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
+<glyph unicode="&#xe178;" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
+<glyph unicode="&#xe179;" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
+<glyph unicode="&#xe180;" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
+<glyph unicode="&#xe181;" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="&#xe182;" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
+<glyph unicode="&#xe183;" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
+<glyph unicode="&#xe184;" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe185;" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
+<glyph unicode="&#xe186;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe187;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe188;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
+<glyph unicode="&#xe189;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
+<glyph unicode="&#xe190;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
+<glyph unicode="&#xe191;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe192;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe193;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
+<glyph unicode="&#xe194;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="&#xe195;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
+<glyph unicode="&#xe197;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe198;" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
+<glyph unicode="&#xe199;" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
+<glyph unicode="&#xe200;" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
+<glyph unicode="&#xe201;" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
+<glyph unicode="&#xe202;" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
+<glyph unicode="&#xe203;" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
+<glyph unicode="&#xe204;" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
+<glyph unicode="&#xe205;" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe206;" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe209;" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
+<glyph unicode="&#xe210;" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe211;" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe212;" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe213;" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe214;" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe215;" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe216;" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
+<glyph unicode="&#xe218;" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
+<glyph unicode="&#xe219;" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
+<glyph unicode="&#xe221;" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe223;" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
+<glyph unicode="&#xe224;" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
+<glyph unicode="&#xe225;" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe226;" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
+<glyph unicode="&#xe227;" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
+<glyph unicode="&#xe230;" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
+<glyph unicode="&#xe231;" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe232;" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe233;" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
+<glyph unicode="&#xe234;" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe235;" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe236;" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe237;" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
+<glyph unicode="&#xe238;" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe239;" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
+<glyph unicode="&#xe240;" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
+<glyph unicode="&#xe241;" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
+<glyph unicode="&#xe242;" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe243;" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
+<glyph unicode="&#xe244;" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
+<glyph unicode="&#xe245;" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
+<glyph unicode="&#xe246;" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
+<glyph unicode="&#xe247;" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe248;" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
+<glyph unicode="&#xe249;" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe250;" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
+<glyph unicode="&#xe251;" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
+<glyph unicode="&#xe252;" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
+<glyph unicode="&#xe253;" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
+<glyph unicode="&#xe254;" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
+<glyph unicode="&#xe255;" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
+<glyph unicode="&#xe256;" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
+<glyph unicode="&#xe257;" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
+<glyph unicode="&#xe258;" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
+<glyph unicode="&#xe259;" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
+<glyph unicode="&#xe260;" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
+<glyph unicode="&#xf8ff;" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
+<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
+<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
+</font>
+</defs></svg> 
\ No newline at end of file
diff --git a/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000..1413fc6
Binary files /dev/null and b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000..9e61285
Binary files /dev/null and b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff differ
diff --git a/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2
new file mode 100644
index 0000000..64539b5
Binary files /dev/null and b/web/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 differ
diff --git a/web/libs/bootstrap-3.3.5/js/bootstrap.min.js b/web/libs/bootstrap-3.3.5/js/bootstrap.min.js
new file mode 100644
index 0000000..133aeec
--- /dev/null
+++ b/web/libs/bootstrap-3.3.5/js/bootstrap.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),
+d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.5",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file
diff --git a/web/libs/humane-3.2.2.min.js b/web/libs/humane-3.2.2.min.js
new file mode 100644
index 0000000..ecbd2fe
--- /dev/null
+++ b/web/libs/humane-3.2.2.min.js
@@ -0,0 +1,11 @@
+;
+/**
+ * humane.js
+ * Humanized Messages for Notifications
+ * @author Marc Harter (@wavded)
+ * @example
+ *   humane.log('hello world');
+ * @license MIT
+ * See more usage examples at: http://wavded.github.com/humane-js/
+ */
+!function(name,context,definition){if(typeof module!=="undefined")module.exports=definition(name,context);else if(typeof define==="function"&&typeof define.amd==="object")define(definition);else context[name]=definition(name,context)}("humane",this,function(name,context){var win=window;var doc=document;var ENV={on:function(el,type,cb){"addEventListener"in win?el.addEventListener(type,cb,false):el.attachEvent("on"+type,cb)},off:function(el,type,cb){"removeEventListener"in win?el.removeEventListener(type,cb,false):el.detachEvent("on"+type,cb)},bind:function(fn,ctx){return function(){fn.apply(ctx,arguments)}},isArray:Array.isArray||function(obj){return Object.prototype.toString.call(obj)==="[object Array]"},config:function(preferred,fallback){return preferred!=null?preferred:fallback},transSupport:false,useFilter:/msie [678]/i.test(navigator.userAgent),_checkTransition:function(){var el=doc.createElement("div");var vendors={webkit:"webkit",Moz:"",O:"o",ms:"MS"};for(var vendor in vendors)if(vendor+"Transition"in el.style){this.vendorPrefix=vendors[vendor];this.transSupport=true}}};ENV._checkTransition();var Humane=function(o){o||(o={});this.queue=[];this.baseCls=o.baseCls||"humane";this.addnCls=o.addnCls||"";this.timeout="timeout"in o?o.timeout:2500;this.waitForMove=o.waitForMove||false;this.clickToClose=o.clickToClose||false;this.timeoutAfterMove=o.timeoutAfterMove||false;this.container=o.container;try{this._setupEl()}catch(e){ENV.on(win,"load",ENV.bind(this._setupEl,this))}};Humane.prototype={constructor:Humane,_setupEl:function(){var el=doc.createElement("div");el.style.display="none";if(!this.container){if(doc.body)this.container=doc.body;else throw"document.body is null"}this.container.appendChild(el);this.el=el;this.removeEvent=ENV.bind(function(){var timeoutAfterMove=ENV.config(this.currentMsg.timeoutAfterMove,this.timeoutAfterMove);if(!timeoutAfterMove){this.remove()}else{setTimeout(ENV.bind(this.remove,this),timeoutAfterMove)}},this);this.transEvent=ENV.bind(this._afterAnimation,this);this._run()},_afterTimeout:function(){if(!ENV.config(this.currentMsg.waitForMove,this.waitForMove))this.remove();else if(!this.removeEventsSet){ENV.on(doc.body,"mousemove",this.removeEvent);ENV.on(doc.body,"click",this.removeEvent);ENV.on(doc.body,"keypress",this.removeEvent);ENV.on(doc.body,"touchstart",this.removeEvent);this.removeEventsSet=true}},_run:function(){if(this._animating||!this.queue.length||!this.el)return;this._animating=true;if(this.currentTimer){clearTimeout(this.currentTimer);this.currentTimer=null}var msg=this.queue.shift();var clickToClose=ENV.config(msg.clickToClose,this.clickToClose);if(clickToClose){ENV.on(this.el,"click",this.removeEvent);ENV.on(this.el,"touchstart",this.removeEvent)}var timeout=ENV.config(msg.timeout,this.timeout);if(timeout>0)this.currentTimer=setTimeout(ENV.bind(this._afterTimeout,this),timeout);if(ENV.isArray(msg.html))msg.html="<ul><li>"+msg.html.join("<li>")+"</ul>";this.el.innerHTML=msg.html;this.currentMsg=msg;this.el.className=this.baseCls;if(ENV.transSupport){this.el.style.display="block";setTimeout(ENV.bind(this._showMsg,this),50)}else{this._showMsg()}},_setOpacity:function(opacity){if(ENV.useFilter){try{this.el.filters.item("DXImageTransform.Microsoft.Alpha").Opacity=opacity*100}catch(err){}}else{this.el.style.opacity=String(opacity)}},_showMsg:function(){var addnCls=ENV.config(this.currentMsg.addnCls,this.addnCls);if(ENV.transSupport){this.el.className=this.baseCls+" "+addnCls+" "+this.baseCls+"-animate"}else{var opacity=0;this.el.className=this.baseCls+" "+addnCls+" "+this.baseCls+"-js-animate";this._setOpacity(0);this.el.style.display="block";var self=this;var interval=setInterval(function(){if(opacity<1){opacity+=.1;if(opacity>1)opacity=1;self._setOpacity(opacity)}else clearInterval(interval)},30)}},_hideMsg:function(){var addnCls=ENV.config(this.currentMsg.addnCls,this.addnCls);if(ENV.transSupport){this.el.className=this.baseCls+" "+addnCls;ENV.on(this.el,ENV.vendorPrefix?ENV.vendorPrefix+"TransitionEnd":"transitionend",this.transEvent)}else{var opacity=1;var self=this;var interval=setInterval(function(){if(opacity>0){opacity-=.1;if(opacity<0)opacity=0;self._setOpacity(opacity)}else{self.el.className=self.baseCls+" "+addnCls;clearInterval(interval);self._afterAnimation()}},30)}},_afterAnimation:function(){if(ENV.transSupport)ENV.off(this.el,ENV.vendorPrefix?ENV.vendorPrefix+"TransitionEnd":"transitionend",this.transEvent);if(this.currentMsg.cb)this.currentMsg.cb();this.el.style.display="none";this._animating=false;this._run()},remove:function(e){var cb=typeof e=="function"?e:null;ENV.off(doc.body,"mousemove",this.removeEvent);ENV.off(doc.body,"click",this.removeEvent);ENV.off(doc.body,"keypress",this.removeEvent);ENV.off(doc.body,"touchstart",this.removeEvent);ENV.off(this.el,"click",this.removeEvent);ENV.off(this.el,"touchstart",this.removeEvent);this.removeEventsSet=false;if(cb&&this.currentMsg)this.currentMsg.cb=cb;if(this._animating)this._hideMsg();else if(cb)cb()},log:function(html,o,cb,defaults){var msg={};if(defaults)for(var opt in defaults)msg[opt]=defaults[opt];if(typeof o=="function")cb=o;else if(o)for(var opt in o)msg[opt]=o[opt];msg.html=html;if(cb)msg.cb=cb;this.queue.push(msg);this._run();return this},spawn:function(defaults){var self=this;return function(html,o,cb){self.log.call(self,html,o,cb,defaults);return self}},create:function(o){return new Humane(o)}};return new Humane});
\ No newline at end of file
diff --git a/web/libs/jquery-2.1.4.min.js b/web/libs/jquery-2.1.4.min.js
new file mode 100644
index 0000000..49990d6
--- /dev/null
+++ b/web/libs/jquery-2.1.4.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\f]' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function qa(){}qa.prototype=d.filters=d.pseudos,d.setFilters=new qa,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function ra(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){
+return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ia={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qa[0].contentDocument,b.write(),b.close(),c=sa(a,b),qa.detach()),ra[a]=c),c}var ua=/^margin/,va=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wa=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)};function xa(a,b,c){var d,e,f,g,h=a.style;return c=c||wa(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),va.test(g)&&ua.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function ya(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),f.removeChild(c),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var za=/^(none|table(?!-c[ea]).+)/,Aa=new RegExp("^("+Q+")(.*)$","i"),Ba=new RegExp("^([+-])=("+Q+")","i"),Ca={position:"absolute",visibility:"hidden",display:"block"},Da={letterSpacing:"0",fontWeight:"400"},Ea=["Webkit","O","Moz","ms"];function Fa(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Ea.length;while(e--)if(b=Ea[e]+c,b in a)return b;return d}function Ga(a,b,c){var d=Aa.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Ha(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ia(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wa(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xa(a,b,f),(0>e||null==e)&&(e=a.style[b]),va.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Ha(a,b,c||(g?"border":"content"),d,f)+"px"}function Ja(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",ta(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xa(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fa(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Ba.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fa(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xa(a,b,d)),"normal"===e&&b in Da&&(e=Da[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?za.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Ca,function(){return Ia(a,b,d)}):Ia(a,b,d):void 0},set:function(a,c,d){var e=d&&wa(a);return Ga(a,c,d?Ha(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=ya(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xa,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ua.test(a)||(n.cssHooks[a+b].set=Ga)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wa(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Ja(this,!0)},hide:function(){return Ja(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Ka(a,b,c,d,e){return new Ka.prototype.init(a,b,c,d,e)}n.Tween=Ka,Ka.prototype={constructor:Ka,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Ka.propHooks[this.prop];return a&&a.get?a.get(this):Ka.propHooks._default.get(this)},run:function(a){var b,c=Ka.propHooks[this.prop];return this.options.duration?this.pos=b=n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ka.propHooks._default.set(this),this}},Ka.prototype.init.prototype=Ka.prototype,Ka.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Ka.propHooks.scrollTop=Ka.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Ka.prototype.init,n.fx.step={};var La,Ma,Na=/^(?:toggle|show|hide)$/,Oa=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pa=/queueHooks$/,Qa=[Va],Ra={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Oa.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Oa.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sa(){return setTimeout(function(){La=void 0}),La=n.now()}function Ta(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ua(a,b,c){for(var d,e=(Ra[b]||[]).concat(Ra["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Va(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||ta(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Na.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?ta(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ua(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wa(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xa(a,b,c){var d,e,f=0,g=Qa.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=La||Sa(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:La||Sa(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wa(k,j.opts.specialEasing);g>f;f++)if(d=Qa[f].call(j,a,k,j.opts))return d;return n.map(k,Ua,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xa,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Ra[c]=Ra[c]||[],Ra[c].unshift(b)},prefilter:function(a,b){b?Qa.unshift(a):Qa.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xa(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pa.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Ta(b,!0),a,d,e)}}),n.each({slideDown:Ta("show"),slideUp:Ta("hide"),slideToggle:Ta("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(La=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),La=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Ma||(Ma=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Ma),Ma=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Ya,Za,$a=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Za:Ya)),
+void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Za={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$a[b]||n.find.attr;$a[b]=function(a,b,d){var e,f;return d||(f=$a[b],$a[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$a[b]=f),e}});var _a=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_a.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ab=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ab," ").indexOf(b)>=0)return!0;return!1}});var bb=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cb=n.now(),db=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var eb=/#.*$/,fb=/([?&])_=[^&]*/,gb=/^(.*?):[ \t]*([^\r\n]*)$/gm,hb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,ib=/^(?:GET|HEAD)$/,jb=/^\/\//,kb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,lb={},mb={},nb="*/".concat("*"),ob=a.location.href,pb=kb.exec(ob.toLowerCase())||[];function qb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function rb(a,b,c,d){var e={},f=a===mb;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function sb(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function tb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function ub(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ob,type:"GET",isLocal:hb.test(pb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":nb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?sb(sb(a,n.ajaxSettings),b):sb(n.ajaxSettings,a)},ajaxPrefilter:qb(lb),ajaxTransport:qb(mb),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=gb.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||ob)+"").replace(eb,"").replace(jb,pb[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=kb.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===pb[1]&&h[2]===pb[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(pb[3]||("http:"===pb[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),rb(lb,k,b,v),2===t)return v;i=n.event&&k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!ib.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(db.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=fb.test(d)?d.replace(fb,"$1_="+cb++):d+(db.test(d)?"&":"?")+"_="+cb++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+nb+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=rb(mb,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=tb(k,v,f)),u=ub(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var vb=/%20/g,wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&").replace(vb,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Bb=0,Cb={},Db={0:200,1223:204},Eb=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Cb)Cb[a]()}),k.cors=!!Eb&&"withCredentials"in Eb,k.ajax=Eb=!!Eb,n.ajaxTransport(function(a){var b;return k.cors||Eb&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Bb;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Cb[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Db[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Cb[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Fb=[],Gb=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Fb.pop()||n.expando+"_"+cb++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Gb.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Gb.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Gb,"$1"+e):b.jsonp!==!1&&(b.url+=(db.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Fb.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Hb=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Hb)return Hb.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Ib=a.document.documentElement;function Jb(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Jb(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Ib;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Ib})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Jb(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=ya(k.pixelPosition,function(a,c){return c?(c=xa(a,b),va.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Kb=a.jQuery,Lb=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Lb),b&&a.jQuery===n&&(a.jQuery=Kb),n},typeof b===U&&(a.jQuery=a.$=n),n});
diff --git a/web/robots.txt b/web/robots.txt
new file mode 100644
index 0000000..214e411
--- /dev/null
+++ b/web/robots.txt
@@ -0,0 +1,4 @@
+# www.robotstxt.org/
+# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
+
+User-agent: *
diff --git a/web/search.osd b/web/search.osd
new file mode 100644
index 0000000..8d63442
--- /dev/null
+++ b/web/search.osd
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Packagist</ShortName>
+    <LongName>Packagist PHP Package Search</LongName>
+    <Description>Use Packagist.org to search for PHP packages.</Description>
+    <Tags>packagist composer php</Tags>
+    <Contact>contact@packagist.org</Contact>
+    <Url type="text/html" template="https://packagist.org/search/?q={searchTerms}"/>
+    <Language>en-us</Language>
+    <OutputEncoding>UTF-8</OutputEncoding>
+    <InputEncoding>UTF-8</InputEncoding>
+    <Image height="180" width="180" type="image/png">https://packagist.org/apple-touch-icon.png</Image>
+    <Image height="16" width="16" type="image/vnd.microsoft.icon">https://packagist.org/favicon.ico</Image>
+</OpenSearchDescription>
diff --git a/web/touch-icon-192x192.png b/web/touch-icon-192x192.png
new file mode 100644
index 0000000..58114b8
Binary files /dev/null and b/web/touch-icon-192x192.png differ
