diff --git a/core/composer.json b/core/composer.json
index c3b9317..f13ec07 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -5,6 +5,9 @@
   "license": "GPL-2.0+",
   "require": {
     "php": ">=5.4.5",
+    "behat/mink": "~1.6",
+    "behat/mink-goutte-driver": "dev-master",
+    "fabpot/goutte": "dev-master",
     "sdboyer/gliph": "0.1.*",
     "symfony/class-loader": "2.6.*",
     "symfony/css-selector": "2.6.*",
diff --git a/core/composer.lock b/core/composer.lock
index e256f14..b73b674 100644
--- a/core/composer.lock
+++ b/core/composer.lock
@@ -7,6 +7,168 @@
     "hash": "1f4fc5da6d6a4621e96ce7d576455f4c",
     "packages": [
         {
+            "name": "behat/mink",
+            "version": "v1.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/minkphp/Mink.git",
+                "reference": "090900a0049c441f1e072bbd837db4079b2250c5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/minkphp/Mink/zipball/090900a0049c441f1e072bbd837db4079b2250c5",
+                "reference": "090900a0049c441f1e072bbd837db4079b2250c5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.1",
+                "symfony/css-selector": "~2.0"
+            },
+            "suggest": {
+                "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)",
+                "behat/mink-goutte-driver": "fast headless driver for any app without JS emulation",
+                "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)",
+                "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Behat\\Mink": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                }
+             ],
+            "description": "Web acceptance testing framework for PHP 5.3",
+            "homepage": "http://mink.behat.org/",
+            "keywords": [
+                "browser",
+                "testing",
+                "web"
+            ],
+            "time": "2014-09-26 09:25:05"
+        },
+        {
+            "name": "behat/mink-browserkit-driver",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                 "url": "https://github.com/minkphp/MinkBrowserKitDriver.git",
+                 "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd"
+             },
+             "dist": {
+                 "type": "zip",
+                 "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/aed8f4a596b79014a75254c3e337511c33e38cbd",
+                 "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd",
+                 "shasum": ""
+             },
+             "require": {
+                 "behat/mink": "~1.6@dev",
+                 "php": ">=5.3.1",
+                 "symfony/browser-kit": "~2.0",
+                 "symfony/dom-crawler": "~2.0"
+             },
+             "require-dev": {
+                 "silex/silex": "~1.2"
+             },
+             "type": "mink-driver",
+             "extra": {
+                 "branch-alias": {
+                     "dev-master": "1.2.x-dev"
+                 }
+             },
+             "autoload": {
+                 "psr-0": {
+                     "Behat\\Mink\\Driver": "src/"
+                 }
+             },
+             "notification-url": "https://packagist.org/downloads/",
+             "license": [
+                 "MIT"
+             ],
+             "authors": [
+                 {
+                     "name": "Konstantin Kudryashov",
+                     "email": "ever.zet@gmail.com",
+                     "homepage": "http://everzet.com"
+                 }
+             ],
+             "description": "Symfony2 BrowserKit driver for Mink framework",
+             "homepage": "http://mink.behat.org/",
+             "keywords": [
+                 "Mink",
+                 "Symfony2",
+                 "browser",
+                 "testing"
+             ],
+             "time": "2014-09-26 11:35:19"
+        },
+        {
+            "name": "behat/mink-goutte-driver",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/minkphp/MinkGoutteDriver.git",
+                "reference": "7f531a03233f60b2292352d4eaa1fc45975aba2c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/7f531a03233f60b2292352d4eaa1fc45975aba2c",
+                "reference": "7f531a03233f60b2292352d4eaa1fc45975aba2c",
+                "shasum": ""
+            },
+            "require": {
+                "behat/mink": "~1.6@dev",
+                "behat/mink-browserkit-driver": "~1.2@dev",
+                "fabpot/goutte": "~1.0.4|~2.0",
+                "php": ">=5.3.1"
+            },
+            "type": "mink-driver",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Behat\\Mink\\Driver": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                }
+            ],
+            "description": "Goutte driver for Mink framework",
+            "homepage": "http://mink.behat.org/",
+            "keywords": [
+                "browser",
+                "goutte",
+                "headless",
+                "testing"
+            ],
+            "time": "2014-12-19 11:23:33"
+        },
+        {
             "name": "doctrine/annotations",
             "version": "v1.2.1",
             "source": {
@@ -571,6 +733,55 @@
             "time": "2014-11-06 08:59:44"
         },
         {
+            "name": "fabpot/goutte",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/FriendsOfPHP/Goutte.git",
+                "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/65ab61eae03d670b93a9044ad2328eb81aa1bde5",
+                "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/guzzle": ">=4,<6",
+                "php": ">=5.4.0",
+                "symfony/browser-kit": "~2.1",
+                "symfony/css-selector": "~2.1",
+                "symfony/dom-crawler": "~2.1"
+            },
+            "type": "application",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Goutte\\": "Goutte"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "A simple PHP Web Scraper",
+            "homepage": "https://github.com/FriendsOfPHP/Goutte",
+            "keywords": [
+                "scraper"
+            ],
+            "time": "2014-11-28 09:48:17"
+        },
+        {
             "name": "guzzlehttp/guzzle",
             "version": "5.0.3",
             "source": {
@@ -1694,6 +1905,61 @@
             "time": "2014-10-20 20:55:17"
         },
         {
+            "name": "symfony/browser-kit",
+            "version": "v2.6.3",
+            "target-dir": "Symfony/Component/BrowserKit",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/BrowserKit.git",
+                "reference": "2ecec44ed5047020c65dd6e4a4b2f3cf13ae3c04"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/2ecec44ed5047020c65dd6e4a4b2f3cf13ae3c04",
+                "reference": "2ecec44ed5047020c65dd6e4a4b2f3cf13ae3c04",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/dom-crawler": "~2.0,>=2.0.5"
+            },
+            "require-dev": {
+                "symfony/css-selector": "~2.0,>=2.0.5",
+                "symfony/process": "~2.0,>=2.0.5"
+            },
+            "suggest": {
+                "symfony/process": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\BrowserKit\\": ""
+                }
+            },
+            "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 BrowserKit Component",
+            "homepage": "http://symfony.com",
+            "time": "2015-01-03 08:01:59"
+        },
+        {
             "name": "symfony/class-loader",
             "version": "v2.6.1",
             "target-dir": "Symfony/Component/ClassLoader",
@@ -1908,6 +2174,59 @@
             "time": "2014-12-03 09:22:11"
         },
         {
+            "name": "symfony/dom-crawler",
+            "version": "v2.6.3",
+            "target-dir": "Symfony/Component/DomCrawler",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/DomCrawler.git",
+                "reference": "26a9eb302decd828990e1015afaa11b78b016073"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/26a9eb302decd828990e1015afaa11b78b016073",
+                "reference": "26a9eb302decd828990e1015afaa11b78b016073",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "symfony/css-selector": "~2.3"
+            },
+            "suggest": {
+                "symfony/css-selector": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\DomCrawler\\": ""
+                }
+            },
+            "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 DomCrawler Component",
+            "homepage": "http://symfony.com",
+            "time": "2015-01-03 08:01:59"
+        },
+        {
             "name": "symfony/event-dispatcher",
             "version": "v2.6.1",
             "target-dir": "Symfony/Component/EventDispatcher",
@@ -2641,6 +2960,8 @@
     "aliases": [],
     "minimum-stability": "stable",
     "stability-flags": {
+        "behat/mink-goutte-driver": 20,
+        "fabpot/goutte": 20,
         "doctrine/common": 20,
         "phpunit/phpunit-mock-objects": 20
     },
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index ff20f28..c733b6f 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -113,7 +113,9 @@
  * @see http://php.net/manual/reserved.variables.server.php
  * @see http://php.net/manual/function.time.php
  */
-define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
+if (!defined('REQUEST_TIME')) {
+  define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
+}
 
 /**
  * Regular expression to match PHP function names.
@@ -141,7 +143,9 @@
  *
  * This strips two levels of directories off the current directory.
  */
-define('DRUPAL_ROOT', dirname(dirname(__DIR__)));
+if (!defined('DRUPAL_ROOT')) {
+  define('DRUPAL_ROOT', dirname(dirname(__DIR__)));
+}
 
 /**
  * Returns the appropriate configuration directory.
@@ -995,7 +999,9 @@ function drupal_valid_test_ua($new_prefix = NULL) {
 
   // Perform a basic check on the User-Agent HTTP request header first. Any
   // inbound request that uses the simpletest UA header needs to be validated.
-  if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $_SERVER['HTTP_USER_AGENT'], $matches)) {
+  $http_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : NULL;
+  $user_agent = isset($_COOKIE['SIMPLETEST_USER_AGENT']) ? $_COOKIE['SIMPLETEST_USER_AGENT'] : $http_user_agent;
+  if (isset($user_agent) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $user_agent, $matches)) {
     list(, $prefix, $time, $salt, $hmac) = $matches;
     $check_string =  $prefix . ';' . $time . ';' . $salt;
     // Read the hash salt prepared by drupal_generate_test_ua().
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index d3d7a04..0446d62 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -299,7 +299,8 @@ function install_begin_request($class_loader, &$install_state) {
   // The user agent header is used to pass a database prefix in the request when
   // running tests. However, for security reasons, it is imperative that no
   // installation be permitted using such a prefix.
-  if ($install_state['interactive'] && strpos($request->server->get('HTTP_USER_AGENT'), 'simpletest') !== FALSE && !drupal_valid_test_ua()) {
+  $user_agent = $request->cookies->get('SIMPLETEST_USER_AGENT') ?: $request->server->get('HTTP_USER_AGENT');
+  if ($install_state['interactive'] && strpos($user_agent, 'simpletest') !== FALSE && !drupal_valid_test_ua()) {
     header($request->server->get('SERVER_PROTOCOL') . ' 403 Forbidden');
     exit;
   }
diff --git a/core/modules/simpletest/src/BrowserTestBase.php b/core/modules/simpletest/src/BrowserTestBase.php
new file mode 100644
index 0000000..3a3cfbe
--- /dev/null
+++ b/core/modules/simpletest/src/BrowserTestBase.php
@@ -0,0 +1,1342 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\simpletest\BrowserTestBase.
+ */
+
+namespace Drupal\simpletest;
+
+use Behat\Mink\Driver\GoutteDriver;
+use Behat\Mink\Element\Element;
+use Behat\Mink\Element\NodeElement;
+use Behat\Mink\Exception\Exception;
+use Behat\Mink\Mink;
+use Behat\Mink\Session;
+use Drupal\Component\Utility\Crypt;
+use Drupal\Component\Utility\Random;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Database\ConnectionNotDefinedException;
+use Drupal\Core\Database\Database;
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Session\AnonymousUserSession;
+use Drupal\Core\Session\UserSession;
+use Drupal\Core\Site\Settings;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Drupal\Core\Test\TestRunnerKernel;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Test case for typical Drupal tests.
+ *
+ * @ingroup testing
+ */
+abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
+
+  /**
+   * Class loader.
+   *
+   * @var object
+   */
+  protected $classLoader;
+
+  /**
+   * The site directory of this test run.
+   *
+   * @var string
+   */
+  protected $siteDirectory;
+
+  /**
+   * The database prefix of this test run.
+   *
+   * @var string
+   */
+  protected $databasePrefix;
+
+  /**
+   * The site directory of the original parent site.
+   *
+   * @var string
+   */
+  protected $originalSiteDirectory;
+
+  /**
+   * Time limit in seconds for the test.
+   */
+  protected $timeLimit = 500;
+
+  /**
+   * The public file directory for the test environment.
+   *
+   * This is set in BrowserTestBase::prepareEnvironment().
+   *
+   * @var string
+   */
+  protected $publicFilesDirectory;
+
+  /**
+   * The private file directory for the test environment.
+   *
+   * This is set in BrowserTestBase::prepareEnvironment().
+   *
+   * @var string
+   */
+  protected $privateFilesDirectory;
+
+  /**
+   * The temp file directory for the test environment.
+   *
+   * This is set in BrowserTestBase::prepareEnvironment().
+   *
+   * @var string
+   */
+  protected $tempFilesDirectory;
+
+  /**
+   * The translation file directory for the test environment.
+   *
+   * This is set in BrowserTestBase::prepareEnvironment().
+   *
+   * @var string
+   */
+  protected $translationFilesDirectory;
+
+  /**
+   * The DrupalKernel instance used in the test.
+   *
+   * @var \Drupal\Core\DrupalKernel
+   */
+  protected $kernel;
+
+  /**
+   * The dependency injection container used in the test.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface
+   */
+  protected $container;
+
+  /**
+   * The config importer that can used in a test.
+   *
+   * @var \Drupal\Core\Config\ConfigImporter
+   */
+  protected $configImporter;
+
+  /**
+   * The random generator.
+   *
+   * @var \Drupal\Component\Utility\Random
+   */
+  protected $randomGenerator;
+
+  /**
+   * The profile to install as a basis for testing.
+   *
+   * @var string
+   */
+  protected $profile = 'testing';
+
+  /**
+   * The current session name, if available.
+   *
+   * @var string
+   */
+  protected $sessionName;
+
+  /**
+   * The current user logged in using the internal browser.
+   *
+   * @var bool
+   */
+  protected $loggedInUser = FALSE;
+
+  /**
+   * The root user.
+   *
+   * @var UserSession
+   */
+  protected $rootUser;
+
+  /**
+   * The config directories used in this test.
+   */
+  protected $configDirectories = array();
+
+  /**
+   * An array of custom translations suitable for drupal_rewrite_settings().
+   *
+   * @var array
+   */
+  protected $customTranslations;
+
+  /**
+   * Mink session manager.
+   *
+   * @var Mink
+   */
+  protected $mink;
+
+  /**
+   * Initializes mink sessions.
+   */
+  protected function initMink() {
+    $driver = new GoutteDriver();
+    $session = new Session($driver);
+    $this->mink = new Mink();
+    $this->mink->registerSession('goutte', $session);
+    $this->mink->setDefaultSessionName('goutte');
+    $this->registerSessions();
+    return $session;
+  }
+
+  /**
+   * Registers additional mink sessions.
+   *
+   * Tests wishing to use a different driver or change the default driver should
+   * override this method.
+   *
+   * @code
+   *   // Register a new session that uses the MinkPonyDriver.
+   *   $pony = new MinkPonyDriver();
+   *   $session = new Session($pony);
+   *   $this->mink->registerSession('pony', $session);
+   * @endcode
+   */
+  protected function registerSessions() {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    global $base_url;
+    parent::setUp();
+
+    // Get and set the domain of the environment we are running our test
+    // coverage against.
+    $base_url = getenv('SIMPLETEST_BASE_URL');
+    if (!$base_url) {
+      throw new \InvalidArgumentException('You must provide a SIMPLETEST_BASE_URL environment variable to run PHPUnit based functional tests.');
+    }
+
+    // Setup $_SERVER variable.
+    $parsed_url = parse_url($base_url);
+    $host = $parsed_url['host'] . (isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '');
+    $path = isset($parsed_url['path']) ? rtrim(rtrim($parsed_url['path']), '/') : '';
+    $port = (isset($parsed_url['port']) ? $parsed_url['port'] : 80);
+    if ($path == '/') {
+      $path = '';
+    }
+    // If the passed URL schema is 'https' then setup the $_SERVER variables
+    // properly so that testing will run under HTTPS.
+    if ($parsed_url['scheme'] === 'https') {
+      $_SERVER['HTTPS'] = 'on';
+    }
+    $_SERVER['HTTP_HOST'] = $host;
+    $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+    $_SERVER['SERVER_ADDR'] = '127.0.0.1';
+    $_SERVER['SERVER_PORT'] = $port;
+    $_SERVER['SERVER_SOFTWARE'] = NULL;
+    $_SERVER['SERVER_NAME'] = 'localhost';
+    $_SERVER['REQUEST_URI'] = $path .'/';
+    $_SERVER['REQUEST_METHOD'] = 'GET';
+    $_SERVER['SCRIPT_NAME'] = $path .'/index.php';
+    $_SERVER['SCRIPT_FILENAME'] = $path .'/index.php';
+    $_SERVER['PHP_SELF'] = $path .'/index.php';
+    $_SERVER['HTTP_USER_AGENT'] = 'Drupal command line';
+
+    // Install drupal test site.
+    $this->prepareEnvironment();
+    $this->installDrupal();
+
+    // Setup Mink.
+    $session = $this->initMink();
+
+    // In order to debug web tests you need to either set a cookie, have the
+    // Xdebug session in the URL or set an environment variable in case of CLI
+    // requests. If the developer listens to connection when running tests, by
+    // default the cookie is not forwarded to the client side, so you cannot
+    // debug the code running on the test site. In order to make debuggers work
+    // this bit of information is forwarded. Make sure that the debugger listens
+    // to at least three external connections.
+    $request = \Drupal::request();
+    $cookie_params = $request->cookies;
+    if ($cookie_params->has('XDEBUG_SESSION')) {
+      $session->setCookie('XDEBUG_SESSION', $cookie_params->get('XDEBUG_SESSION'));
+    }
+    // For CLI requests, the information is stored in $_SERVER.
+    $server = $request->server;
+    if ($server->has('XDEBUG_CONFIG')) {
+      // $_SERVER['XDEBUG_CONFIG'] has the form "key1=value1 key2=value2 ...".
+      $pairs = explode(' ', $server->get('XDEBUG_CONFIG'));
+      foreach ($pairs as $pair) {
+        list($key, $value) = explode('=', $pair);
+        // Account for key-value pairs being separated by multiple spaces.
+        if (trim($key, ' ') == 'idekey') {
+          $session->setCookie('XDEBUG_SESSION', trim($value, ' '));
+        }
+      }
+    }
+  }
+
+  /**
+   * Ensures test files are deletable within file_unmanaged_delete_recursive().
+   *
+   * Some tests chmod generated files to be read only. During
+   * BrowserTestBase::cleanupEnvironment() and other cleanup operations,
+   * these files need to get deleted too.
+   *
+   * @param string $path
+   *   The file path.
+   */
+  public static function filePreDeleteCallback($path) {
+    chmod($path, 0700);
+  }
+
+  /**
+   * Clean up the simpletest environment.
+   */
+  protected function cleanupEnvironment() {
+    // Remove all prefixed tables.
+    $original_connection_info = Database::getConnectionInfo('simpletest_original_default');
+    $original_prefix = $original_connection_info['default']['prefix']['default'];
+    $test_connection_info = Database::getConnectionInfo('default');
+    $test_prefix = $test_connection_info['default']['prefix']['default'];
+    if ($original_prefix != $test_prefix) {
+      $tables = Database::getConnection()->schema()->findTables($test_prefix . '%');
+      $prefix_length = strlen($test_prefix);
+      foreach ($tables as $table) {
+        if (Database::getConnection()->schema()->dropTable(substr($table, $prefix_length))) {
+          unset($tables[$table]);
+        }
+      }
+    }
+
+    // Delete test site directory.
+    file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function tearDown() {
+    parent::tearDown();
+
+    // Destroy the testing kernel.
+    if (isset($this->kernel)) {
+      $this->cleanupEnvironment();
+      $this->kernel->shutdown();
+    }
+
+    // Ensure that internal logged in variable is reset.
+    $this->loggedInUser = FALSE;
+
+    if ($this->mink) {
+      $this->mink->stopSessions();
+    }
+  }
+
+  /**
+   * Returns Mink session.
+   *
+   * @param string|null $name
+   *   (optional) Name of the session OR active session will be used.
+   *
+   * @return \Behat\Mink\Session
+   *   The active mink session object.
+   */
+  public function getSession($name = NULL) {
+    return $this->mink->getSession($name);
+  }
+
+  /**
+   * Returns Mink assert session.
+   *
+   * @param string|null $name
+   *   (optional) Name of the session. Defaults to the active session.
+   *
+   * @return \Drupal\simpletest\WebAssert
+   *   A new web-assert option for asserting the presence of elements with.
+   */
+  public function assertSession($name = NULL) {
+    return new WebAssert($this->getSession($name));
+  }
+
+  /**
+   * Prepare for a request to testing site.
+   *
+   * The testing site is protected via a SIMPLETEST_USER_AGENT cookie that
+   * is checked by drupal_valid_test_ua().
+   *
+   * @see drupal_valid_test_ua()
+   */
+  protected function prepareRequest() {
+    $session = $this->getSession();
+    $session->setCookie('SIMPLETEST_USER_AGENT', drupal_generate_test_ua($this->databasePrefix));
+  }
+
+  /**
+   * Retrieves a Drupal path or an absolute path.
+   *
+   * @param string $path
+   *   Drupal path or URL to load into internal browser
+   * @param array $options
+   *   (optional) Options to be forwarded to the url generator.
+   *
+   * @return string
+   *   The retrieved HTML string, also available as $this->getRawContent()
+   */
+  protected function drupalGet($path, array $options = array()) {
+    $options['absolute'] = TRUE;
+
+    // The URL generator service is not necessarily available yet; e.g., in
+    // interactive installer tests.
+    if ($this->container->has('url_generator')) {
+      $url = $this->container->get('url_generator')->generateFromPath($path, $options);
+    }
+    else {
+      $url = $this->getAbsoluteUrl($path);
+    }
+    $session = $this->getSession();
+
+    $this->prepareRequest();
+    $session->visit($url);
+    $out = $session->getPage()->getContent();
+
+    // Ensure that any changes to variables in the other thread are picked up.
+    $this->refreshVariables();
+
+    return $out;
+  }
+
+  /**
+   * Takes a path and returns an absolute path.
+   *
+   * @param string $path
+   *   A path from the internal browser content.
+   *
+   * @return string
+   *   The $path with $base_url prepended, if necessary.
+   */
+  protected function getAbsoluteUrl($path) {
+    global $base_url, $base_path;
+
+    $parts = parse_url($path);
+    if (empty($parts['host'])) {
+      // Ensure that we have a string (and no xpath object).
+      $path = (string) $path;
+      // Strip $base_path, if existent.
+      $length = strlen($base_path);
+      if (substr($path, 0, $length) === $base_path) {
+        $path = substr($path, $length);
+      }
+      // Ensure that we have an absolute path.
+      if ($path[0] !== '/') {
+        $path = '/' . $path;
+      }
+      // Finally, prepend the $base_url.
+      $path = $base_url . $path;
+    }
+    return $path;
+  }
+
+  /**
+   * Creates a user with a given set of permissions.
+   *
+   * @param array $permissions
+   *   (optional) Array of permission names to assign to user. Note that the
+   *   user always has the default permissions derived from the
+   *   "authenticated users" role.
+   * @param string $name
+   *   (optional) The user name.
+   *
+   * @return \Drupal\user\Entity\User|false
+   *   A fully loaded user object with passRaw property, or FALSE if account
+   *   creation fails.
+   */
+  protected function drupalCreateUser(array $permissions = array(), $name = NULL) {
+    // Create a role with the given permission set, if any.
+    $rid = FALSE;
+    if ($permissions) {
+      $rid = $this->drupalCreateRole($permissions);
+      if (!$rid) {
+        return FALSE;
+      }
+    }
+
+    // Create a user assigned to that role.
+    $edit = array();
+    $edit['name'] = !empty($name) ? $name : $this->randomMachineName();
+    $edit['mail'] = $edit['name'] . '@example.com';
+    $edit['pass'] = user_password();
+    $edit['status'] = 1;
+    if ($rid) {
+      $edit['roles'] = array($rid);
+    }
+
+    $account = entity_create('user', $edit);
+    $account->save();
+
+    $this->assertNotNull($account->id(), String::format('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])));
+    if (!$account->id()) {
+      return FALSE;
+    }
+
+    // Add the raw password so that we can log in as this user.
+    $account->passRaw = $edit['pass'];
+    return $account;
+  }
+
+  /**
+   * Creates a role with specified permissions.
+   *
+   * @param array $permissions
+   *   Array of permission names to assign to role.
+   * @param string $rid
+   *   (optional) The role ID (machine name). Defaults to a random name.
+   * @param string $name
+   *   (optional) The label for the role. Defaults to a random string.
+   * @param int $weight
+   *   (optional) The weight for the role. Defaults NULL so that entity_create()
+   *   sets the weight to maximum + 1.
+   *
+   * @return string
+   *   Role ID of newly created role, or FALSE if role creation failed.
+   */
+  protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NULL, $weight = NULL) {
+    // Generate a random, lowercase machine name if none was passed.
+    if (!isset($rid)) {
+      $rid = strtolower($this->randomMachineName(8));
+    }
+    // Generate a random label.
+    if (!isset($name)) {
+      // In the role UI role names are trimmed and random string can start or
+      // end with a space.
+      $name = trim($this->randomString(8));
+    }
+
+    // Check the all the permissions strings are valid.
+    if (!$this->checkPermissions($permissions)) {
+      return FALSE;
+    }
+
+    // Create new role.
+    /* @var \Drupal\user\RoleInterface $role */
+    $role = entity_create('user_role', array(
+      'id' => $rid,
+      'label' => $name,
+    ));
+    if (!is_null($weight)) {
+      $role->set('weight', $weight);
+    }
+    $result = $role->save();
+
+    $this->assertSame($result, SAVED_NEW, String::format('Created role ID @rid with name @name.', array(
+      '@name' => var_export($role->label(), TRUE),
+      '@rid' => var_export($role->id(), TRUE),
+    )));
+
+    if ($result === SAVED_NEW) {
+      // Grant the specified permissions to the role, if any.
+      if (!empty($permissions)) {
+        user_role_grant_permissions($role->id(), $permissions);
+        $assigned_permissions = entity_load('user_role', $role->id())->getPermissions();
+        $missing_permissions = array_diff($permissions, $assigned_permissions);
+        if ($missing_permissions) {
+          $this->fail(String::format('Failed to create permissions: @perms', array('@perms' => implode(', ', $missing_permissions))));
+        }
+      }
+      return $role->id();
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Generates a unique random string containing letters and numbers.
+   *
+   * Do not use this method when testing unvalidated user input. Instead, use
+   * \Drupal\simpletest\TestBase::randomString().
+   *
+   * @param int $length
+   *   (optional) Length of random string to generate.
+   *
+   * @return string
+   *   Randomly generated unique string.
+   *
+   * @see \Drupal\Component\Utility\Random::name()
+   */
+  public function randomMachineName($length = 8) {
+    return $this->getRandomGenerator()->name($length, TRUE);
+  }
+
+  /**
+   * Generates a pseudo-random string of ASCII characters of codes 32 to 126.
+   *
+   * Do not use this method when special characters are not possible (e.g., in
+   * machine or file names that have already been validated); instead, use
+   * \Drupal\simpletest\TestBase::randomMachineName(). If $length is greater
+   * than 2 the random string will include at least one ampersand ('&')
+   * character to ensure coverage for special characters and avoid the
+   * introduction of random test failures.
+   *
+   * @param int $length
+   *   (optional) Length of random string to generate.
+   *
+   * @return string
+   *   Pseudo-randomly generated unique string including special characters.
+   *
+   * @see \Drupal\Component\Utility\Random::string()
+   */
+  public function randomString($length = 8) {
+    if ($length < 3) {
+      return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate'));
+    }
+
+    // To prevent the introduction of random test failures, ensure that the
+    // returned string contains a character that needs to be escaped in HTML by
+    // injecting an ampersand into it.
+    $replacement_pos = floor($length / 2);
+    // Remove 1 from the length to account for the ampersand character.
+    $string = $this->getRandomGenerator()->string($length - 1, TRUE, array($this, 'randomStringValidate'));
+    return substr_replace($string, '&', $replacement_pos, 0);
+  }
+
+  /**
+   * Checks whether a given list of permission names is valid.
+   *
+   * @param array $permissions
+   *   The permission names to check.
+   *
+   * @return bool
+   *   TRUE if the permissions are valid, FALSE otherwise.
+   */
+  protected function checkPermissions(array $permissions) {
+    $available = array_keys(\Drupal::service('user.permissions')->getPermissions());
+    $valid = TRUE;
+    foreach ($permissions as $permission) {
+      if (!in_array($permission, $available)) {
+        $this->fail(String::format('Invalid permission %permission.', array('%permission' => $permission)));
+        $valid = FALSE;
+      }
+    }
+    return $valid;
+  }
+
+  /**
+   * Logs in a user using the mink browser.
+   *
+   * If a user is already logged in, then the current user is logged out before
+   * logging in the specified user.
+   *
+   * Please note that neither the current user nor the passed-in user object is
+   * populated with data of the logged in user. If you need full access to the
+   * user object after logging in, it must be updated manually. If you also need
+   * access to the plain-text password of the user (set by drupalCreateUser()),
+   * e.g. to log in the same user again, then it must be re-assigned manually.
+   * For example:
+   * @code
+   *   // Create a user.
+   *   $account = $this->drupalCreateUser(array());
+   *   $this->drupalLogin($account);
+   *   // Load real user object.
+   *   $pass_raw = $account->passRaw;
+   *   $account = user_load($account->id());
+   *   $account->passRaw = $pass_raw;
+   * @endcode
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   User object representing the user to log in.
+   *
+   * @see drupalCreateUser()
+   */
+  protected function drupalLogin(AccountInterface $account) {
+    if ($this->loggedInUser) {
+      $this->drupalLogout();
+    }
+
+    $this->drupalGet('user');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm(array(
+      'name' => $account->getUsername(),
+      'pass' => $account->passRaw,
+    ), t('Log in'));
+
+    // @see BrowserTestBase::drupalUserIsLoggedIn()
+    $account->sessionId = $this->getSession()->getCookie(session_name());
+    $this->assertTrue($this->drupalUserIsLoggedIn($account), String::format('User %name successfully logged in.', array('name' => $account->getUsername())));
+
+    $this->loggedInUser = $account;
+    $this->container->get('current_user')->setAccount($account);
+  }
+
+  /**
+   * Logs a user out of the internal browser and confirms.
+   *
+   * Confirms logout by checking the login page.
+   */
+  protected function drupalLogout() {
+    // Make a request to the logout page, and redirect to the user page, the
+    // idea being if you were properly logged out you should be seeing a login
+    // screen.
+    $assert_session = $this->assertSession();
+    $this->drupalGet('user/logout', array('query' => array('destination' => 'user')));
+    $assert_session->statusCodeEquals(200);
+    $assert_session->fieldExists('name');
+    $assert_session->fieldExists('pass');
+
+    // @see BrowserTestBase::drupalUserIsLoggedIn()
+    unset($this->loggedInUser->sessionId);
+    $this->loggedInUser = FALSE;
+    $this->container->get('current_user')->setAccount(new AnonymousUserSession());
+  }
+
+  /**
+   * Fills and submit a form.
+   *
+   * @param array $edit
+   *   Field data in an associative array. Changes the current input fields
+   *   (where possible) to the values indicated.
+   *
+   *   A checkbox can be set to TRUE to be checked and should be set to FALSE to
+   *   be unchecked.
+   * @param string $submit
+   *   Value of the submit button whose click is to be emulated. For example,
+   *   t('Save'). The processing of the request depends on this value. For
+   *   example, a form may have one button with the value t('Save') and another
+   *   button with the value t('Delete'), and execute different code depending
+   *   on which one is clicked.
+   * @param string $form_html_id
+   *   (optional) HTML ID of the form to be submitted. On some pages
+   *   there are many identical forms, so just using the value of the submit
+   *   button is not enough. For example: 'trigger-node-presave-assign-form'.
+   *   Note that this is not the Drupal $form_id, but rather the HTML ID of the
+   *   form, which is typically the same thing but with hyphens replacing the
+   *   underscores.
+   */
+  protected function submitForm($edit, $submit, $form_html_id = NULL) {
+    $assert_session = $this->assertSession();
+
+    // Get the form.
+    if (isset($form_html_id)) {
+      $form = $assert_session->elementExists('xpath', "//form[@id='" . $form_html_id . "']");
+      $submit_button = $assert_session->buttonExists($submit, $form);
+    }
+    else {
+      $submit_button = $assert_session->buttonExists($submit);
+      $form = $assert_session->elementExists('xpath', './ancestor::form', $submit_button);
+    }
+
+    // Edit the form values.
+    foreach ($edit as $name => $value) {
+      $field = $assert_session->fieldExists($name, $form);
+      $field->setValue($value);
+    }
+
+    // Submit form.
+    $this->prepareRequest();
+    $submit_button->press();
+
+    // Ensure that any changes to variables in the other thread are picked up.
+    $this->refreshVariables();
+  }
+
+  /**
+   * Helper function to get the options of select field.
+   *
+   * @param NodeElement|string $select
+   *   Name, ID, or Label of select field to assert.
+   * @param Element $container
+   *   (optional) Container element to check against. Defaults to current page.
+   *
+   * @return array
+   *   Associative array of option keys and values.
+   */
+  protected function getOptions($select, Element $container = NULL) {
+    if (is_string($select)) {
+      $select = $this->assertSession()->selectExists($select, $container);
+    }
+    $options = [];
+    /* @var \Behat\Mink\Element\NodeElement $option */
+    foreach ($select->findAll('xpath', '//option') as $option) {
+      $label = $option->getText();
+      $value = $option->getAttribute('value') ?: $label;
+      $options[$value] = $label;
+    }
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function run(\PHPUnit_Framework_TestResult $result = NULL) {
+    if ($result === NULL) {
+      $result = $this->createResult();
+    }
+
+    parent::run($result);
+    return $result;
+  }
+
+  /**
+   * Override to use Mink exceptions.
+   *
+   * @return mixed
+   *   Either a test result or NULL.
+   * @throws \PHPUnit_Framework_AssertionFailedError
+   *   When exception was thrown inside the test.
+   */
+  protected function runTest() {
+    try {
+      return parent::runTest();
+    }
+    catch (Exception $e) {
+      throw new \PHPUnit_Framework_AssertionFailedError($e->getMessage());
+    }
+  }
+
+  /**
+   * Generates a unique random string containing letters and numbers.
+   *
+   * Do not use this method when testing unvalidated user input. Instead, use
+   * \Drupal\simpletest\TestBase::randomString().
+   *
+   * @param int $length
+   *   (optional) Length of random string to generate.
+   *
+   * @return string
+   *   Randomly generated unique string.
+   *
+   * @see \Drupal\Component\Utility\Random::name()
+   */
+  public function randomName($length = 8) {
+    return $this->getRandomGenerator()->name($length, TRUE);
+  }
+
+  /**
+   * Gets the random generator for the utility methods.
+   *
+   * @return \Drupal\Component\Utility\Random
+   *   The random generator
+   */
+  protected function getRandomGenerator() {
+    if (!is_object($this->randomGenerator)) {
+      $this->randomGenerator = new Random();
+    }
+    return $this->randomGenerator;
+  }
+
+  /**
+   * Installs drupal into the simpletest site.
+   */
+  public function installDrupal() {
+    // Define information about the user 1 account.
+    $this->rootUser = new UserSession(array(
+      'uid' => 1,
+      'name' => 'admin',
+      'mail' => 'admin@example.com',
+      'passRaw' => $this->randomName(),
+    ));
+
+    // Some tests (SessionTest and SessionHttpsTest) need to examine whether the
+    // proper session cookies were set on a response. Because the child site
+    // uses the same session name as the test runner, it is necessary to make
+    // that available to test-methods.
+    $this->sessionName = session_name();
+
+    // Get parameters for install_drupal() before removing global variables.
+    $parameters = $this->installParameters();
+
+    // Prepare installer settings that are not install_drupal() parameters.
+    // Copy and prepare an actual settings.php, so as to resemble a regular
+    // installation.
+    // Not using File API; a potential error must trigger a PHP warning.
+    $directory = DRUPAL_ROOT . '/' . $this->siteDirectory;
+    copy(DRUPAL_ROOT . '/sites/default/default.settings.php', $directory . '/settings.php');
+    copy(DRUPAL_ROOT . '/sites/default/default.services.yml', $directory . '/services.yml');
+
+    // All file system paths are created by System module during installation.
+    // @see system_requirements()
+    // @see TestBase::prepareEnvironment()
+    $settings['settings']['file_public_path'] = (object) array(
+      'value' => $this->publicFilesDirectory,
+      'required' => TRUE,
+    );
+    $this->writeSettings($settings);
+    // Allow for test-specific overrides.
+    $settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSiteDirectory . '/settings.testing.php';
+    if (file_exists($settings_testing_file)) {
+      // Copy the testing-specific settings.php overrides in place.
+      copy($settings_testing_file, $directory . '/settings.testing.php');
+      // Add the name of the testing class to settings.php and include the
+      // testing specific overrides.
+      file_put_contents($directory . '/settings.php', "\n\$test_class = '" . get_class($this) . "';\n" . 'include DRUPAL_ROOT . \'/\' . $site_path . \'/settings.testing.php\';' . "\n", FILE_APPEND);
+    }
+    $settings_services_file = DRUPAL_ROOT . '/' . $this->originalSiteDirectory . '/testing.services.yml';
+    if (file_exists($settings_services_file)) {
+      // Copy the testing-specific service overrides in place.
+      copy($settings_services_file, $directory . '/services.yml');
+    }
+
+    // Since Drupal is bootstrapped already, install_begin_request() will not
+    // bootstrap into DRUPAL_BOOTSTRAP_CONFIGURATION (again). Hence, we have to
+    // reload the newly written custom settings.php manually.
+    Settings::initialize(DRUPAL_ROOT, $directory, $this->classLoader);
+
+    // Execute the non-interactive installer.
+    require_once DRUPAL_ROOT . '/core/includes/install.core.inc';
+    install_drupal($parameters);
+
+    // Import new settings.php written by the installer.
+    Settings::initialize(DRUPAL_ROOT, $directory, $this->classLoader);
+    foreach ($GLOBALS['config_directories'] as $type => $path) {
+      $this->configDirectories[$type] = $path;
+    }
+
+    // After writing settings.php, the installer removes write permissions
+    // from the site directory. To allow drupal_generate_test_ua() to write
+    // a file containing the private key for drupal_valid_test_ua(), the site
+    // directory has to be writable.
+    // TestBase::restoreEnvironment() will delete the entire site directory.
+    // Not using File API; a potential error must trigger a PHP warning.
+    chmod($directory, 0777);
+
+    $request = \Drupal::request();
+    $this->kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod', TRUE);
+    $this->kernel->prepareLegacyRequest($request);
+    // Force the container to be built from scratch instead of loaded from the
+    // disk. This forces us to not accidently load the parent site.
+    $container = $this->kernel->rebuildContainer();
+
+    $config = $container->get('config.factory');
+
+    // Manually create and configure private and temporary files directories.
+    // While these could be preset/enforced in settings.php like the public
+    // files directory above, some tests expect them to be configurable in the
+    // UI. If declared in settings.php, they would no longer be configurable.
+    file_prepare_directory($this->privateFilesDirectory, FILE_CREATE_DIRECTORY);
+    file_prepare_directory($this->tempFilesDirectory, FILE_CREATE_DIRECTORY);
+    $config->get('system.file')
+      ->set('path.private', $this->privateFilesDirectory)
+      ->set('path.temporary', $this->tempFilesDirectory)
+      ->save();
+
+    // Manually configure the test mail collector implementation to prevent
+    // tests from sending out emails and collect them in state instead.
+    // While this should be enforced via settings.php prior to installation,
+    // some tests expect to be able to test mail system implementations.
+    $config->get('system.mail')
+      ->set('interface.default', 'test_mail_collector')
+      ->save();
+
+    // By default, verbosely display all errors and disable all production
+    // environment optimizations for all tests to avoid needless overhead and
+    // ensure a sane default experience for test authors.
+    // @see https://drupal.org/node/2259167
+    $config->get('system.logging')
+      ->set('error_level', 'verbose')
+      ->save();
+    $config->get('system.performance')
+      ->set('css.preprocess', FALSE)
+      ->set('js.preprocess', FALSE)
+      ->save();
+
+    // Collect modules to install.
+    $class = get_class($this);
+    $modules = array();
+    while ($class) {
+      if (property_exists($class, 'modules')) {
+        $modules = array_merge($modules, $class::$modules);
+      }
+      $class = get_parent_class($class);
+    }
+    if ($modules) {
+      $modules = array_unique($modules);
+      $success = $container->get('module_installer')->install($modules, TRUE);
+      $this->assertTrue($success, String::format('Enabled modules: %modules', array('%modules' => implode(', ', $modules))));
+      $this->rebuildContainer();
+    }
+
+    // Reset/rebuild all data structures after enabling the modules, primarily
+    // to synchronize all data structures and caches between the test runner and
+    // the child site.
+    // Affects e.g. file_get_stream_wrappers().
+    // @see \Drupal\Core\DrupalKernel::bootCode()
+    // @todo Test-specific setUp() methods may set up further fixtures; find a
+    //   way to execute this after setUp() is done, or to eliminate it entirely.
+    $this->resetAll();
+    $this->kernel->prepareLegacyRequest($request);
+  }
+
+  /**
+   * Returns the parameters that will be used when Simpletest installs Drupal.
+   *
+   * @see install_drupal()
+   * @see install_state_defaults()
+   */
+  protected function installParameters() {
+    $connection_info = Database::getConnectionInfo();
+    $driver = $connection_info['default']['driver'];
+    $connection_info['default']['prefix'] = $connection_info['default']['prefix']['default'];
+    unset($connection_info['default']['driver']);
+    unset($connection_info['default']['namespace']);
+    unset($connection_info['default']['pdo']);
+    unset($connection_info['default']['init_commands']);
+    $parameters = array(
+      'interactive' => FALSE,
+      'parameters' => array(
+        'profile' => $this->profile,
+        'langcode' => 'en',
+      ),
+      'forms' => array(
+        'install_settings_form' => array(
+          'driver' => $driver,
+          $driver => $connection_info['default'],
+        ),
+        'install_configure_form' => array(
+          'site_name' => 'Drupal',
+          'site_mail' => 'simpletest@example.com',
+          'account' => array(
+            'name' => $this->rootUser->name,
+            'mail' => $this->rootUser->getEmail(),
+            'pass' => array(
+              'pass1' => $this->rootUser->passRaw,
+              'pass2' => $this->rootUser->passRaw,
+            ),
+          ),
+          // form_type_checkboxes_value() requires NULL instead of FALSE values
+          // for programmatic form submissions to disable a checkbox.
+          'update_status_module' => array(
+            1 => NULL,
+            2 => NULL,
+          ),
+        ),
+      ),
+    );
+    return $parameters;
+  }
+
+  /**
+   * Generates a database prefix for running tests.
+   *
+   * The database prefix is used by prepareEnvironment() to setup a public files
+   * directory for the test to be run, which also contains the PHP error log,
+   * which is written to in case of a fatal error. Since that directory is based
+   * on the database prefix, all tests (even unit tests) need to have one, in
+   * order to access and read the error log.
+   *
+   * The generated database table prefix is used for the Drupal installation
+   * being performed for the test. It is also used by the cookie value of
+   * SIMPLETEST_USER_AGENT by the mink browser. During early Drupal bootstrap,
+   * the cookie is parsed, and if it matches, all database queries use
+   * the database table prefix that has been generated here.
+   *
+   * @see drupal_valid_test_ua()
+   * @see BrowserTestBase::prepareEnvironment()
+   */
+  private function prepareDatabasePrefix() {
+    // Ensure that the generated test site directory does not exist already,
+    // which may happen with a large amount of concurrent threads and
+    // long-running tests.
+    do {
+      $suffix = mt_rand(100000, 999999);
+      $this->siteDirectory = 'sites/simpletest/' . $suffix;
+      $this->databasePrefix = 'simpletest' . $suffix;
+    } while (is_dir(DRUPAL_ROOT . '/' . $this->siteDirectory));
+  }
+
+  /**
+   * Changes the database connection to the prefixed one.
+   *
+   * @see BrowserTestBase::prepareEnvironment()
+   */
+  private function changeDatabasePrefix() {
+    if (empty($this->databasePrefix)) {
+      $this->prepareDatabasePrefix();
+    }
+
+    // Clone the current connection and replace the current prefix.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::renameConnection('default', 'simpletest_original_default');
+    foreach ($connection_info as $target => $value) {
+      // Replace the full table prefix definition to ensure that no table
+      // prefixes of the test runner leak into the test.
+      $connection_info[$target]['prefix'] = array(
+        'default' => $value['prefix']['default'] . $this->databasePrefix,
+      );
+    }
+    Database::addConnectionInfo('default', 'default', $connection_info['default']);
+  }
+
+  /**
+   * Prepares the current environment for running the test.
+   *
+   * Also sets up new resources for the testing environment, such as the public
+   * filesystem and configuration directories.
+   *
+   * This method is private as it must only be called once by
+   * BrowserTestBase::setUp() (multiple invocations for the same test would have
+   * unpredictable consequences) and it must not be callable or overridable by
+   * test classes.
+   */
+  protected function prepareEnvironment() {
+    // Bootstrap Drupal so we can use Drupal's built in functions.
+    $this->classLoader = require __DIR__ . '/../../../vendor/autoload.php';
+    $request = Request::createFromGlobals();
+    $kernel = TestRunnerKernel::createFromRequest($request, $this->classLoader);
+    // TestRunnerKernel expects the working directory to be DRUPAL_ROOT.
+    chdir(DRUPAL_ROOT);
+    $kernel->prepareLegacyRequest($request);
+    $this->prepareDatabasePrefix();
+
+    $this->originalSiteDirectory = $kernel->findSitePath($request);
+
+    // Create test directory ahead of installation so fatal errors and debug
+    // information can be logged during installation process.
+    file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+
+    // Prepare filesystem directory paths.
+    $this->publicFilesDirectory = $this->siteDirectory . '/files';
+    $this->privateFilesDirectory = $this->siteDirectory . '/private';
+    $this->tempFilesDirectory = $this->siteDirectory . '/temp';
+    $this->translationFilesDirectory = $this->siteDirectory . '/translations';
+
+    // Ensure the configImporter is refreshed for each test.
+    $this->configImporter = NULL;
+
+    // Unregister all custom stream wrappers of the parent site.
+    $wrappers = \Drupal::service('stream_wrapper_manager')->getWrappers(StreamWrapperInterface::ALL);
+    foreach ($wrappers as $scheme => $info) {
+      stream_wrapper_unregister($scheme);
+    }
+
+    // Reset statics.
+    drupal_static_reset();
+
+    // Ensure there is no service container.
+    $this->container = NULL;
+    \Drupal::setContainer(NULL);
+
+    // Unset globals.
+    unset($GLOBALS['config_directories']);
+    unset($GLOBALS['config']);
+    unset($GLOBALS['conf']);
+
+    // Log fatal errors.
+    ini_set('log_errors', 1);
+    ini_set('error_log', DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log');
+
+    // Change the database prefix.
+    $this->changeDatabasePrefix();
+
+    // After preparing the environment and changing the database prefix, we are
+    // in a valid test environment.
+    drupal_valid_test_ua($this->databasePrefix);
+
+    // Reset settings.
+    new Settings(array(
+      // For performance, simply use the database prefix as hash salt.
+      'hash_salt' => $this->databasePrefix,
+    ));
+
+    drupal_set_time_limit($this->timeLimit);
+  }
+
+  /**
+   * Returns the database connection to the site running Simpletest.
+   *
+   * @return \Drupal\Core\Database\Connection
+   *   The database connection to use for inserting assertions.
+   */
+  public static function getDatabaseConnection() {
+    // Check whether there is a test runner connection.
+    // @see run-tests.sh
+    try {
+      $connection = Database::getConnection('default', 'test-runner');
+    }
+    catch (ConnectionNotDefinedException $e) {
+      // Check whether there is a backup of the original default connection.
+      // @see BrowserTestBase::prepareEnvironment()
+      try {
+        $connection = Database::getConnection('default', 'simpletest_original_default');
+      }
+      catch (ConnectionNotDefinedException $e) {
+        // If BrowserTestBase::prepareEnvironment() or
+        // BrowserTestBase::restoreEnvironment() failed, the test-specific
+        // database connection does not exist yet/anymore, so fall back to the
+        // default of the (UI) test runner.
+        $connection = Database::getConnection('default', 'default');
+      }
+    }
+    return $connection;
+  }
+
+  /**
+   * Rewrites the settings.php file of the test site.
+   *
+   * @param array $settings
+   *   An array of settings to write out, in the format expected by
+   *   drupal_rewrite_settings().
+   *
+   * @see drupal_rewrite_settings()
+   */
+  protected function writeSettings(array $settings) {
+    include_once DRUPAL_ROOT . '/core/includes/install.inc';
+    $filename = $this->siteDirectory . '/settings.php';
+
+    error_log($filename);
+
+    // system_requirements() removes write permissions from settings.php
+    // whenever it is invoked.
+    // Not using File API; a potential error must trigger a PHP warning.
+    chmod($filename, 0666);
+    drupal_rewrite_settings($settings, $filename);
+  }
+
+  /**
+   * Rebuilds \Drupal::getContainer().
+   *
+   * Use this to build a new kernel and service container. For example, when the
+   * list of enabled modules is changed via the internal browser, in which case
+   * the test process still contains an old kernel and service container with an
+   * old module list.
+   *
+   * @see BrowserTestBase::prepareEnvironment()
+   * @see BrowserTestBase::restoreEnvironment()
+   *
+   * @todo Fix https://www.drupal.org/node/2021959 so that module enable/disable
+   *   changes are immediately reflected in \Drupal::getContainer(). Until then,
+   *   tests can invoke this workaround when requiring services from newly
+   *   enabled modules to be immediately available in the same request.
+   */
+  protected function rebuildContainer() {
+    // Maintain the current global request object.
+    $request = \Drupal::request();
+    // Rebuild the kernel and bring it back to a fully bootstrapped state.
+    $this->container = $this->kernel->rebuildContainer();
+
+    // Make sure the url generator has a request object, otherwise calls to
+    // $this->drupalGet() will fail.
+    $this->prepareRequestForGenerator();
+  }
+
+  /**
+   * Creates a mock request and sets it on the generator.
+   *
+   * This is used to manipulate how the generator generates paths during tests.
+   * It also ensures that calls to $this->drupalGet() will work when running
+   * from run-tests.sh because the url generator no longer looks at the global
+   * variables that are set there but relies on getting this information from a
+   * request object.
+   *
+   * @param bool $clean_urls
+   *   Whether to mock the request using clean urls.
+   * @param array $override_server_vars
+   *   An array of server variables to override.
+   *
+   * @return Request
+   *   The mocked request object.
+   */
+  protected function prepareRequestForGenerator($clean_urls = TRUE, $override_server_vars = array()) {
+    $request = Request::createFromGlobals();
+    $server = $request->server->all();
+    if (basename($server['SCRIPT_FILENAME']) != basename($server['SCRIPT_NAME'])) {
+      // We need this for when the test is executed by run-tests.sh.
+      // @todo Remove this once run-tests.sh has been converted to use a Request
+      //   object.
+      $cwd = getcwd();
+      $server['SCRIPT_FILENAME'] = $cwd . '/' . basename($server['SCRIPT_NAME']);
+      $base_path = rtrim($server['REQUEST_URI'], '/');
+    }
+    else {
+      $base_path = $request->getBasePath();
+    }
+    if ($clean_urls) {
+      $request_path = $base_path ? $base_path . '/user' : 'user';
+    }
+    else {
+      $request_path = $base_path ? $base_path . '/index.php/user' : '/index.php/user';
+    }
+    $server = array_merge($server, $override_server_vars);
+
+    $request = Request::create($request_path, 'GET', array(), array(), array(), $server);
+    $this->container->get('request_stack')->push($request);
+
+    // The request context is normally set by the router_listener from within
+    // its KernelEvents::REQUEST listener. In the simpletest parent site this
+    // event is not fired, therefore it is necessary to updated the request
+    // context manually here.
+    $this->container->get('router.request_context')->fromRequest($request);
+
+    return $request;
+  }
+
+  /**
+   * Resets all data structures after having enabled new modules.
+   *
+   * This method is called by \Drupal\simpletest\WebTestBase::setUp() after
+   * enabling the requested modules. It must be called again when additional
+   * modules are enabled later.
+   */
+  protected function resetAll() {
+    // Clear all database and static caches and rebuild data structures.
+    drupal_flush_all_caches();
+    $this->container = \Drupal::getContainer();
+
+    // Reset static variables and reload permissions.
+    $this->refreshVariables();
+  }
+
+  /**
+   * Refreshes in-memory configuration and state information.
+   *
+   * Useful after a page request is made that changes configuration or state in
+   * a different thread.
+   *
+   * In other words calling a settings page with $this->drupalPostForm() with a
+   * changed value would update configuration to reflect that change, but in the
+   * thread that made the call (thread running the test) the changed values
+   * would not be picked up.
+   *
+   * This method clears the cache and loads a fresh copy.
+   */
+  protected function refreshVariables() {
+    // Clear the tag cache.
+    // @todo Replace drupal_static() usage within classes and provide a
+    //   proper interface for invoking reset() on a cache backend:
+    //   https://www.drupal.org/node/2311945.
+    drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache');
+    drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::deletedTags');
+    drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::invalidatedTags');
+    foreach (Cache::getBins() as $backend) {
+      if (is_callable(array($backend, 'reset'))) {
+        $backend->reset();
+      }
+    }
+
+    $this->container->get('config.factory')->reset();
+    $this->container->get('state')->resetCache();
+  }
+
+  /**
+   * Returns whether a given user account is logged in.
+   *
+   * @param \Drupal\user\UserInterface $account
+   *   The user account object to check.
+   *
+   * @return bool
+   *   Return TRUE if the user is logged in, FALSE otherwise.
+   */
+  protected function drupalUserIsLoggedIn($account) {
+    if (!isset($account->sessionId)) {
+      return FALSE;
+    }
+    // The session ID is hashed before being stored in the database.
+    // @see \Drupal\Core\Session\SessionHandler::read()
+    return (bool) db_query("SELECT sid FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid AND u.default_langcode = 1 WHERE s.sid = :sid", array(':sid' => Crypt::hashBase64($account->sessionId)))->fetchField();
+  }
+
+}
diff --git a/core/modules/simpletest/src/Form/SimpletestTestForm.php b/core/modules/simpletest/src/Form/SimpletestTestForm.php
index 03f16f5..5c8265a 100644
--- a/core/modules/simpletest/src/Form/SimpletestTestForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestTestForm.php
@@ -179,6 +179,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
+    global $base_url;
     // Test discovery does not run upon form submission.
     simpletest_classloader_register();
 
@@ -209,6 +210,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       }
     }
     if (!empty($tests_list)) {
+      putenv('SIMPLETEST_BASE_URL=' . $base_url);
       $test_id = simpletest_run_tests($tests_list, 'drupal');
       $form_state->setRedirect(
         'simpletest.result_form',
diff --git a/core/modules/simpletest/src/WebAssert.php b/core/modules/simpletest/src/WebAssert.php
new file mode 100644
index 0000000..daea904
--- /dev/null
+++ b/core/modules/simpletest/src/WebAssert.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\simpletest\WebAssert.
+ */
+
+namespace Drupal\simpletest;
+
+use Behat\Mink\WebAssert as MinkWebAssert;
+use Behat\Mink\Element\TraversableElement;
+use Behat\Mink\Exception\ElementNotFoundException;
+
+/**
+ * Defines a class with methods for asserting presence of elements during tests.
+ */
+class WebAssert extends MinkWebAssert {
+
+  /**
+   * Checks that specific button exists on the current page.
+   *
+   * @param string $button
+   *   One of id|name|label|value for the button.
+   * @param \Behat\Mink\Element\TraversableElement $container
+   *   The document to check against.
+   *
+   * @return \Behat\Mink\Element\NodeElement
+   *   The matching element.
+   *
+   * @throws \Behat\Mink\Exception\ElementNotFoundException
+   *   When the element doesn't exist.
+   */
+  public function buttonExists($button, TraversableElement $container = NULL) {
+    $container = $container ?: $this->session->getPage();
+    $node = $container->findButton($button);
+
+    if (NULL === $node) {
+      throw new ElementNotFoundException($this->session, 'button', 'id|name|label|value', $button);
+    }
+
+    return $node;
+  }
+
+  /**
+   * Checks that specific select field exists on the current page.
+   *
+   * @param string $select
+   *   One of id|name|label|value for the select field.
+   * @param \Behat\Mink\Element\TraversableElement $container
+   *   The document to check against.
+   *
+   * @return \Behat\Mink\Element\NodeElement
+   *   The matching element
+   *
+   * @throws \Behat\Mink\Exception\ElementNotFoundException
+   *   When the element doesn't exist.
+   */
+  public function selectExists($select, TraversableElement $container = NULL) {
+    $container = $container ?: $this->session->getPage();
+    $node = $container->find('named', array(
+      'select',
+      $this->session->getSelectorsHandler()->xpathLiteral($select),
+    ));
+
+    if (NULL === $node) {
+      throw new ElementNotFoundException($this->session, 'select', 'id|name|label|value', $select);
+    }
+
+    return $node;
+  }
+}
diff --git a/core/modules/simpletest/tests/src/Functional/BrowserTestBaseTest.php b/core/modules/simpletest/tests/src/Functional/BrowserTestBaseTest.php
new file mode 100644
index 0000000..6f4c473
--- /dev/null
+++ b/core/modules/simpletest/tests/src/Functional/BrowserTestBaseTest.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\Tests\simpletest\Functional\BrowserTestBaseTest.
+ */
+
+namespace Drupal\Tests\simpletest\Functional;
+
+use Drupal\simpletest\BrowserTestBase;
+
+/**
+ * Tests BrowserTestBase functionality.
+ *
+ * @group simpletest
+ * @group modern
+ */
+class BrowserTestBaseTest extends BrowserTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('test_page_test', 'form_test');
+
+  /**
+   * Tests basic page test.
+   */
+  public function testGoTo() {
+    $account = $this->drupalCreateUser();
+    $this->drupalLogin($account);
+
+    // Visit a Drupal page that requires login.
+    $this->drupalGet('/test-page');
+    $this->assertSession()->statusCodeEquals(200);
+
+    // Test page contains some text.
+    $this->assertSession()->pageTextContains('Test page text.');
+  }
+
+  /**
+   * Tests basic form functionality.
+   */
+  public function testForm() {
+    // Ensure the proper response code for a _form route.
+    $this->drupalGet('/form-test/object-builder');
+    $this->assertSession()->statusCodeEquals(200);
+
+    // Ensure the form and text field exist.
+    $this->assertSession()->elementExists('css', 'form#form-test-form-test-object');
+    $this->assertSession()->fieldExists('bananas');
+
+    $edit = ['bananas' => 'green'];
+    $this->submitForm($edit, 'Save', 'form-test-form-test-object');
+
+    $config_factory = $this->container->get('config.factory');
+    $value = $config_factory->get('form_test.object')->get('bananas');
+    $this->assertSame('green', $value);
+  }
+
+}
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index aa4acfd..78f2f0e 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -6,9 +6,10 @@
     <ini name="error_reporting" value="32767"/>
     <!-- Do not limit the amount of memory tests take to run. -->
     <ini name="memory_limit" value="-1"/>
+    <env name="SIMPLETEST_BASE_URL" value="http://localhost"/>
   </php>
   <testsuites>
-    <testsuite name="Drupal Unit Test Suite">
+    <testsuite name="unit">
       <directory>./tests</directory>
       <directory>./modules/*/tests</directory>
       <directory>../modules</directory>
@@ -20,6 +21,15 @@
       <!-- Exclude special-case files from config's test modules. -->
       <exclude>./modules/config/tests/config_test/src</exclude>
     </testsuite>
+    <testsuite name="functional">
+      <directory>./modules/*/tests/src/Functional</directory>
+      <!-- Exclude Composer's vendor directory so we don't run tests there. -->
+      <exclude>./vendor</exclude>
+      <!-- Exclude Drush tests. -->
+      <exclude>./drush/tests</exclude>
+      <!-- Exclude special-case files from config's test modules. -->
+      <exclude>./modules/config/tests/config_test/src</exclude>
+    </testsuite>
   </testsuites>
   <!-- Filter for coverage reports. -->
   <filter>
@@ -32,3 +42,4 @@
      </whitelist>
   </filter>
 </phpunit>
+
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 2fa3b9e..ae95b81 100644
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -339,6 +339,17 @@ function simpletest_script_init() {
     }
   }
 
+  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
+    $base_url = 'https://';
+  }
+  else {
+    $base_url = 'http://';
+  }
+  $base_url .= $host;
+  if ($path !== '') {
+    $base_url .= $path;
+  }
+  putenv('SIMPLETEST_BASE_URL=' . $base_url);
   $_SERVER['HTTP_HOST'] = $host;
   $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
   $_SERVER['SERVER_ADDR'] = '127.0.0.1';
diff --git a/core/vendor/behat/mink-browserkit-driver/.travis.yml b/core/vendor/behat/mink-browserkit-driver/.travis.yml
new file mode 100644
index 0000000..838fc4e
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/.travis.yml
@@ -0,0 +1,20 @@
+language: php
+
+php: [5.3, 5.4, 5.5, 5.6, hhvm]
+
+matrix:
+  include:
+    - php: 5.5
+      env: SYMFONY_VERSION='2.3.*'
+    - php: 5.5
+      env: SYMFONY_VERSION='2.5.*@dev'
+
+before_script:
+  - sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require -n --no-update symfony/symfony=$SYMFONY_VERSION; fi;'
+  - composer install -n --prefer-source
+
+script: phpunit -v --coverage-clover=coverage.clover
+
+after_script:
+  - wget https://scrutinizer-ci.com/ocular.phar
+  - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
diff --git a/core/vendor/behat/mink-browserkit-driver/CHANGELOG.md b/core/vendor/behat/mink-browserkit-driver/CHANGELOG.md
new file mode 100644
index 0000000..774663f
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/CHANGELOG.md
@@ -0,0 +1,33 @@
+1.2.0 / 2014-09-26
+==================
+
+BC break:
+
+* Changed the behavior of `getValue` for checkboxes according to the BC break in Mink 1.6
+
+New features:
+
+* Implemented `getOuterHtml`
+* Added the support of manipulating forms without submit buttons
+* Added support of any request headers instead of supporting only a few hardcoded ones
+* Added support of any BrowserKit client using `filterResponse` when using BrowserKit 2.3+
+* Added the support of reset buttons
+* Implemented `submitForm`
+* Implemented `isSelected`
+
+Bug fixes:
+
+* Fixed the support of options without value attribute in `isSelected` and `getValue`
+* Added the support of radio buttons in `isChecked`
+* Fixed the submission of empty textarea fields
+* Refactored the handling of request headers to ensure they are reset when resetting the driver
+* Fixed the handling of buttons to submit only for submit buttons rather than all buttons
+* Fixed the code to throw exceptions rather than triggering a fatal error for invalid usages of the driver
+* Fixed the removal of cookies
+* Fixed the submission of form fields with same name and without id
+* Fixed `getAttribute` to return `null` for missing attributes rather than an empty string
+
+Testing:
+
+* Updated the testsuite to use the new Mink 1.6 driver testsuite
+* Added testing on HHVM
diff --git a/core/vendor/behat/mink-browserkit-driver/LICENSE b/core/vendor/behat/mink-browserkit-driver/LICENSE
new file mode 100644
index 0000000..3365ae6
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012-2013 Konstantin Kudryashov <ever.zet@gmail.com>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/core/vendor/behat/mink-browserkit-driver/README.md b/core/vendor/behat/mink-browserkit-driver/README.md
new file mode 100644
index 0000000..fc36e41
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/README.md
@@ -0,0 +1,54 @@
+Mink BrowserKit Driver
+======================
+
+[![Latest Stable Version](https://poser.pugx.org/behat/mink-browserkit-driver/v/stable.png)](https://packagist.org/packages/behat/mink-browserkit-driver)
+[![Latest Unstable Version](https://poser.pugx.org/behat/mink-browserkit-driver/v/unstable.svg)](https://packagist.org/packages/behat/mink-browserkit-driver)
+[![Total Downloads](https://poser.pugx.org/behat/mink-browserkit-driver/downloads.png)](https://packagist.org/packages/behat/mink-browserkit-driver)
+[![Build Status](https://travis-ci.org/Behat/MinkBrowserKitDriver.svg?branch=master)](https://travis-ci.org/Behat/MinkBrowserKitDriver)
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/badges/quality-score.png?s=0443d284940e099ea560eb39b6b2fcdc5d4e7f29)](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/)
+[![Code Coverage](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/badges/coverage.png?s=48960c4495488ab0b7d310b62322f017497f5bfa)](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/)
+[![License](https://poser.pugx.org/behat/mink-browserkit-driver/license.svg)](https://packagist.org/packages/behat/mink-browserkit-driver)
+
+Usage Example
+-------------
+
+``` php
+<?php
+
+use Behat\Mink\Mink,
+    Behat\Mink\Session,
+    Behat\Mink\Driver\BrowserKitDriver;
+
+use Symfony\Component\HttpKernel\Client;
+
+$app  = require_once(__DIR__.'/app.php'); // Silex app
+
+$mink = new Mink(array(
+    'silex' => new Session(new BrowserKitDriver(new Client($app))),
+));
+
+$mink->getSession('silex')->getPage()->findLink('Chat')->click();
+```
+
+Installation
+------------
+
+``` json
+{
+    "require": {
+        "behat/mink":                   "~1.5",
+        "behat/mink-browserkit-driver": "~1.1"
+    }
+}
+```
+
+``` bash
+$> curl -sS https://getcomposer.org/installer | php
+$> php composer.phar install
+```
+
+Maintainers
+-----------
+
+* Konstantin Kudryashov [everzet](http://github.com/everzet)
+* Other [awesome developers](https://github.com/Behat/MinkBrowserKitDriver/graphs/contributors)
diff --git a/core/vendor/behat/mink-browserkit-driver/composer.json b/core/vendor/behat/mink-browserkit-driver/composer.json
new file mode 100644
index 0000000..c0c5df9
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/composer.json
@@ -0,0 +1,45 @@
+{
+    "name":         "behat/mink-browserkit-driver",
+    "description":  "Symfony2 BrowserKit driver for Mink framework",
+    "keywords":     ["Symfony2", "Mink", "testing", "browser"],
+    "homepage":     "http://mink.behat.org/",
+    "type":         "mink-driver",
+    "license":      "MIT",
+
+    "authors": [
+        {
+            "name":      "Konstantin Kudryashov",
+            "email":     "ever.zet@gmail.com",
+            "homepage":  "http://everzet.com"
+        }
+    ],
+
+    "require": {
+        "php":                  ">=5.3.1",
+        "behat/mink":           "~1.6@dev",
+        "symfony/browser-kit":  "~2.0",
+        "symfony/dom-crawler":  "~2.0"
+    },
+
+    "require-dev": {
+        "silex/silex": "~1.2"
+    },
+
+    "autoload": {
+        "psr-0": {
+            "Behat\\Mink\\Driver": "src/"
+        }
+    },
+
+    "autoload-dev": {
+        "psr-4": {
+            "Behat\\Mink\\Tests\\Driver\\": "tests"
+        }
+    },
+
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.2.x-dev"
+        }
+    }
+}
diff --git a/core/vendor/behat/mink-browserkit-driver/phpunit.xml.dist b/core/vendor/behat/mink-browserkit-driver/phpunit.xml.dist
new file mode 100644
index 0000000..4f9b932
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/phpunit.xml.dist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit colors="true" bootstrap="vendor/behat/mink/driver-testsuite/bootstrap.php">
+    <php>
+        <var name="driver_config_factory" value="Behat\Mink\Tests\Driver\BrowserKitConfig::getInstance" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Driver test suite">
+            <directory>tests</directory>
+            <directory>vendor/behat/mink/driver-testsuite/tests/Basic</directory>
+            <directory>vendor/behat/mink/driver-testsuite/tests/Form</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./src/Behat/Mink/Driver</directory>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/core/vendor/behat/mink-browserkit-driver/src/Behat/Mink/Driver/BrowserKitDriver.php b/core/vendor/behat/mink-browserkit-driver/src/Behat/Mink/Driver/BrowserKitDriver.php
new file mode 100644
index 0000000..28b99dc
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/src/Behat/Mink/Driver/BrowserKitDriver.php
@@ -0,0 +1,949 @@
+<?php
+
+/*
+ * This file is part of the Behat\Mink.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Driver;
+
+use Behat\Mink\Element\NodeElement;
+use Behat\Mink\Exception\DriverException;
+use Behat\Mink\Exception\UnsupportedDriverActionException;
+use Behat\Mink\Session;
+use Symfony\Component\BrowserKit\Client;
+use Symfony\Component\BrowserKit\Cookie;
+use Symfony\Component\BrowserKit\Request;
+use Symfony\Component\BrowserKit\Response;
+use Symfony\Component\DomCrawler\Crawler;
+use Symfony\Component\DomCrawler\Field\ChoiceFormField;
+use Symfony\Component\DomCrawler\Field\FileFormField;
+use Symfony\Component\DomCrawler\Field\FormField;
+use Symfony\Component\DomCrawler\Field\InputFormField;
+use Symfony\Component\DomCrawler\Field\TextareaFormField;
+use Symfony\Component\DomCrawler\Form;
+use Symfony\Component\HttpFoundation\Request as HttpFoundationRequest;
+use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse;
+use Symfony\Component\HttpKernel\Client as HttpKernelClient;
+
+/**
+ * Symfony2 BrowserKit driver.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class BrowserKitDriver extends CoreDriver
+{
+    private $session;
+    private $client;
+
+    /**
+     * @var Form[]
+     */
+    private $forms = array();
+    private $serverParameters = array();
+    private $started = false;
+    private $removeScriptFromUrl = false;
+    private $removeHostFromUrl = false;
+
+    /**
+     * Initializes BrowserKit driver.
+     *
+     * @param Client      $client  BrowserKit client instance
+     * @param string|null $baseUrl Base URL for HttpKernel clients
+     */
+    public function __construct(Client $client, $baseUrl = null)
+    {
+        $this->client = $client;
+        $this->client->followRedirects(true);
+
+        if ($baseUrl !== null && $client instanceof HttpKernelClient) {
+            $client->setServerParameter('SCRIPT_FILENAME', parse_url($baseUrl, PHP_URL_PATH));
+        }
+    }
+
+    /**
+     * Returns BrowserKit HTTP client instance.
+     *
+     * @return Client
+     */
+    public function getClient()
+    {
+        return $this->client;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSession(Session $session)
+    {
+        $this->session = $session;
+    }
+
+    /**
+     * Tells driver to remove hostname from URL.
+     *
+     * @param Boolean $remove
+     *
+     * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead.
+     */
+    public function setRemoveHostFromUrl($remove = true)
+    {
+        trigger_error(
+            'setRemoveHostFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.',
+            E_USER_DEPRECATED
+        );
+        $this->removeHostFromUrl = (bool) $remove;
+    }
+
+    /**
+     * Tells driver to remove script name from URL.
+     *
+     * @param Boolean $remove
+     *
+     * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead.
+     */
+    public function setRemoveScriptFromUrl($remove = true)
+    {
+        trigger_error(
+            'setRemoveScriptFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.',
+            E_USER_DEPRECATED
+        );
+        $this->removeScriptFromUrl = (bool) $remove;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start()
+    {
+        $this->started = true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isStarted()
+    {
+        return $this->started;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function stop()
+    {
+        $this->reset();
+        $this->started = false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reset()
+    {
+        // Restarting the client resets the cookies and the history
+        $this->client->restart();
+        $this->forms = array();
+        $this->serverParameters = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function visit($url)
+    {
+        $this->client->request('GET', $this->prepareUrl($url), array(), array(), $this->serverParameters);
+        $this->forms = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCurrentUrl()
+    {
+        if (method_exists($this->client, 'getInternalRequest')) {
+            $request = $this->client->getInternalRequest();
+        } else {
+            // BC layer for BrowserKit 2.2.x and older
+            $request = $this->client->getRequest();
+
+            if (null !== $request && !$request instanceof Request && !$request instanceof HttpFoundationRequest) {
+                throw new DriverException(sprintf(
+                    'The BrowserKit client returned an unsupported request implementation: %s. Please upgrade your BrowserKit package to 2.3 or newer.',
+                    get_class($request)
+                ));
+            }
+        }
+
+        if ($request === null) {
+            throw new DriverException('Unable to access the request before visiting a page');
+        }
+
+        return $request->getUri();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reload()
+    {
+        $this->client->reload();
+        $this->forms = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function forward()
+    {
+        $this->client->forward();
+        $this->forms = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function back()
+    {
+        $this->client->back();
+        $this->forms = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setBasicAuth($user, $password)
+    {
+        if (false === $user) {
+            unset($this->serverParameters['PHP_AUTH_USER'], $this->serverParameters['PHP_AUTH_PW']);
+
+            return;
+        }
+
+        $this->serverParameters['PHP_AUTH_USER'] = $user;
+        $this->serverParameters['PHP_AUTH_PW'] = $password;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setRequestHeader($name, $value)
+    {
+        $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
+        $name = str_replace('-', '_', strtoupper($name));
+
+        // CONTENT_* are not prefixed with HTTP_ in PHP when building $_SERVER
+        if (!isset($contentHeaders[$name])) {
+            $name = 'HTTP_' . $name;
+        }
+
+        $this->serverParameters[$name] = $value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getResponseHeaders()
+    {
+        return $this->getResponse()->getHeaders();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setCookie($name, $value = null)
+    {
+        if (null === $value) {
+            $this->deleteCookie($name);
+
+            return;
+        }
+
+        $jar = $this->client->getCookieJar();
+        $jar->set(new Cookie($name, $value));
+    }
+
+    /**
+     * Deletes a cookie by name.
+     *
+     * @param string $name Cookie name.
+     */
+    private function deleteCookie($name)
+    {
+        $path = $this->getCookiePath();
+        $jar = $this->client->getCookieJar();
+
+        do {
+            if (null !== $jar->get($name, $path)) {
+                $jar->expire($name, $path);
+            }
+
+            $path = preg_replace('/.$/', '', $path);
+        } while ($path);
+    }
+
+    /**
+     * Returns current cookie path.
+     *
+     * @return string
+     */
+    private function getCookiePath()
+    {
+        $path = dirname(parse_url($this->getCurrentUrl(), PHP_URL_PATH));
+
+        if ('\\' === DIRECTORY_SEPARATOR) {
+            $path = str_replace('\\', '/', $path);
+        }
+
+        return $path;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCookie($name)
+    {
+        // Note that the following doesn't work well because
+        // Symfony\Component\BrowserKit\CookieJar stores cookies by name,
+        // path, AND domain and if you don't fill them all in correctly then
+        // you won't get the value that you're expecting.
+        //
+        // $jar = $this->client->getCookieJar();
+        //
+        // if (null !== $cookie = $jar->get($name)) {
+        //     return $cookie->getValue();
+        // }
+
+        $allValues = $this->client->getCookieJar()->allValues($this->getCurrentUrl());
+
+        if (isset($allValues[$name])) {
+            return $allValues[$name];
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStatusCode()
+    {
+        return $this->getResponse()->getStatus();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContent()
+    {
+        return $this->getResponse()->getContent();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function find($xpath)
+    {
+        $nodes = $this->getCrawler()->filterXPath($xpath);
+
+        $elements = array();
+        foreach ($nodes as $i => $node) {
+            $elements[] = new NodeElement(sprintf('(%s)[%d]', $xpath, $i + 1), $this->session);
+        }
+
+        return $elements;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTagName($xpath)
+    {
+        return $this->getCrawlerNode($this->getFilteredCrawler($xpath))->nodeName;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getText($xpath)
+    {
+        $text = $this->getFilteredCrawler($xpath)->text();
+        $text = str_replace("\n", ' ', $text);
+        $text = preg_replace('/ {2,}/', ' ', $text);
+
+        return trim($text);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getHtml($xpath)
+    {
+        // cut the tag itself (making innerHTML out of outerHTML)
+        return preg_replace('/^\<[^\>]+\>|\<[^\>]+\>$/', '', $this->getOuterHtml($xpath));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOuterHtml($xpath)
+    {
+        $node = $this->getCrawlerNode($this->getFilteredCrawler($xpath));
+
+        return $node->ownerDocument->saveXML($node);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttribute($xpath, $name)
+    {
+        $node = $this->getFilteredCrawler($xpath);
+
+        if ($this->getCrawlerNode($node)->hasAttribute($name)) {
+            return $node->attr($name);
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getValue($xpath)
+    {
+        if (in_array($this->getAttribute($xpath, 'type'), array('submit', 'image', 'button'))) {
+            return $this->getAttribute($xpath, 'value');
+        }
+
+        $node = $this->getCrawlerNode($this->getFilteredCrawler($xpath));
+
+        if ('option' === $node->tagName) {
+            return $this->getOptionValue($node);
+        }
+
+        try {
+            $field = $this->getFormField($xpath);
+        } catch (\InvalidArgumentException $e) {
+            return $this->getAttribute($xpath, 'value');
+        }
+
+        return $field->getValue();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setValue($xpath, $value)
+    {
+        $this->getFormField($xpath)->setValue($value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function check($xpath)
+    {
+        $this->getCheckboxField($xpath)->tick();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function uncheck($xpath)
+    {
+        $this->getCheckboxField($xpath)->untick();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function selectOption($xpath, $value, $multiple = false)
+    {
+        $field = $this->getFormField($xpath);
+
+        if (!$field instanceof ChoiceFormField) {
+            throw new DriverException(sprintf('Impossible to select an option on the element with XPath "%s" as it is not a select or radio input', $xpath));
+        }
+
+        if ($multiple) {
+            $oldValue   = (array) $field->getValue();
+            $oldValue[] = $value;
+            $value      = $oldValue;
+        }
+
+        $field->select($value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isSelected($xpath)
+    {
+        $optionValue = $this->getOptionValue($this->getCrawlerNode($this->getFilteredCrawler($xpath)));
+        $selectField = $this->getFormField('(' . $xpath . ')/ancestor-or-self::*[local-name()="select"]');
+        $selectValue = $selectField->getValue();
+
+        return is_array($selectValue) ? in_array($optionValue, $selectValue) : $optionValue == $selectValue;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function click($xpath)
+    {
+        $node = $this->getFilteredCrawler($xpath);
+        $crawlerNode = $this->getCrawlerNode($node);
+        $tagName = $crawlerNode->nodeName;
+
+        if ('a' === $tagName) {
+            $this->client->click($node->link());
+            $this->forms = array();
+        } elseif ($this->canSubmitForm($crawlerNode)) {
+            $this->submit($node->form());
+        } elseif ($this->canResetForm($crawlerNode)) {
+            $this->resetForm($crawlerNode);
+        } else {
+            $message = sprintf('%%s supports clicking on links and buttons only. But "%s" provided', $tagName);
+
+            throw new UnsupportedDriverActionException($message, $this);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isChecked($xpath)
+    {
+        $field = $this->getFormField($xpath);
+
+        if (!$field instanceof ChoiceFormField || 'select' === $field->getType()) {
+            throw new DriverException(sprintf('Impossible to get the checked state of the element with XPath "%s" as it is not a checkbox or radio input', $xpath));
+        }
+
+        if ('checkbox' === $field->getType()) {
+            return $field->hasValue();
+        }
+
+        $radio = $this->getCrawlerNode($this->getFilteredCrawler($xpath));
+
+        return $radio->getAttribute('value') === $field->getValue();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function attachFile($xpath, $path)
+    {
+        $field = $this->getFormField($xpath);
+
+        if (!$field instanceof FileFormField) {
+            throw new DriverException(sprintf('Impossible to attach a file on the element with XPath "%s" as it is not a file input', $xpath));
+        }
+
+        $field->upload($path);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function submitForm($xpath)
+    {
+        $crawler = $this->getFilteredCrawler($xpath);
+
+        $this->submit($crawler->form());
+    }
+
+    /**
+     * @return Response
+     *
+     * @throws DriverException If there is not response yet
+     */
+    protected function getResponse()
+    {
+        if (!method_exists($this->client, 'getInternalResponse')) {
+            $implementationResponse = $this->client->getResponse();
+
+            if (null === $implementationResponse) {
+                throw new DriverException('Unable to access the response before visiting a page');
+            }
+
+            return $this->convertImplementationResponse($implementationResponse);
+        }
+
+        $response = $this->client->getInternalResponse();
+
+        if (null === $response) {
+            throw new DriverException('Unable to access the response before visiting a page');
+        }
+
+        return $response;
+    }
+
+    /**
+     * Gets the BrowserKit Response for legacy BrowserKit versions.
+     *
+     * Before 2.3.0, there was no Client::getInternalResponse method, and the
+     * return value of Client::getResponse can be anything when the implementation
+     * uses Client::filterResponse because of a bad choice done in BrowserKit and
+     * kept for BC reasons (the Client::getInternalResponse method has been added
+     * to solve it).
+     *
+     * This implementation supports client which don't rely Client::filterResponse
+     * and clients which use an HttpFoundation Response (like the HttpKernel client).
+     *
+     * @param object $response the response specific to the BrowserKit implementation
+     *
+     * @return Response
+     *
+     * @throws DriverException If the response cannot be converted to a BrowserKit response
+     */
+    private function convertImplementationResponse($response)
+    {
+        if ($response instanceof Response) {
+            return $response;
+        }
+
+        // due to a bug, the HttpKernel client implementation returns the HttpFoundation response
+        // The conversion logic is copied from Symfony\Component\HttpKernel\Client::filterResponse
+        if ($response instanceof HttpFoundationResponse) {
+            $headers = $response->headers->all();
+            if ($response->headers->getCookies()) {
+                $cookies = array();
+                foreach ($response->headers->getCookies() as $cookie) {
+                    $cookies[] = new Cookie(
+                        $cookie->getName(),
+                        $cookie->getValue(),
+                        $cookie->getExpiresTime(),
+                        $cookie->getPath(),
+                        $cookie->getDomain(),
+                        $cookie->isSecure(),
+                        $cookie->isHttpOnly()
+                    );
+                }
+                $headers['Set-Cookie'] = $cookies;
+            }
+
+            // this is needed to support StreamedResponse
+            ob_start();
+            $response->sendContent();
+            $content = ob_get_clean();
+
+            return new Response($content, $response->getStatusCode(), $headers);
+        }
+
+        throw new DriverException(sprintf(
+            'The BrowserKit client returned an unsupported response implementation: %s. Please upgrade your BrowserKit package to 2.3 or newer.',
+            get_class($response)
+        ));
+    }
+
+    /**
+     * Prepares URL for visiting.
+     * Removes "*.php/" from urls and then passes it to BrowserKitDriver::visit().
+     *
+     * @param string $url
+     *
+     * @return string
+     */
+    protected function prepareUrl($url)
+    {
+        $replacement = ($this->removeHostFromUrl ? '' : '$1') . ($this->removeScriptFromUrl ? '' : '$2');
+
+        return preg_replace('#(https?\://[^/]+)(/[^/\.]+\.php)?#', $replacement, $url);
+    }
+
+    /**
+     * Returns form field from XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return FormField
+     *
+     * @throws DriverException
+     */
+    protected function getFormField($xpath)
+    {
+        $fieldNode = $this->getCrawlerNode($this->getFilteredCrawler($xpath));
+        $fieldName = str_replace('[]', '', $fieldNode->getAttribute('name'));
+
+        $formNode = $this->getFormNode($fieldNode);
+        $formId = $this->getFormNodeId($formNode);
+
+        if (!isset($this->forms[$formId])) {
+            $this->forms[$formId] = new Form($formNode, $this->getCurrentUrl());
+        }
+
+        if (is_array($this->forms[$formId][$fieldName])) {
+            return $this->forms[$formId][$fieldName][$this->getFieldPosition($fieldNode)];
+        }
+
+        return $this->forms[$formId][$fieldName];
+    }
+
+    /**
+     * Returns the checkbox field from xpath query, ensuring it is valid.
+     *
+     * @param string $xpath
+     *
+     * @return ChoiceFormField
+     *
+     * @throws DriverException when the field is not a checkbox
+     */
+    private function getCheckboxField($xpath)
+    {
+        $field = $this->getFormField($xpath);
+
+        if (!$field instanceof ChoiceFormField) {
+            throw new DriverException(sprintf('Impossible to check the element with XPath "%s" as it is not a checkbox', $xpath));
+        }
+
+        return $field;
+    }
+
+    /**
+     * @param \DOMElement $element
+     *
+     * @return \DOMElement
+     *
+     * @throws DriverException if the form node cannot be found
+     */
+    private function getFormNode(\DOMElement $element)
+    {
+        if ($element->hasAttribute('form')) {
+            $formId = $element->getAttribute('form');
+            $formNode = $element->ownerDocument->getElementById($formId);
+
+            if (null === $formNode || 'form' !== $formNode->nodeName) {
+                throw new DriverException(sprintf('The selected node has an invalid form attribute (%s).', $formId));
+            }
+
+            return $formNode;
+        }
+
+        $formNode = $element;
+
+        do {
+            // use the ancestor form element
+            if (null === $formNode = $formNode->parentNode) {
+                throw new DriverException('The selected node does not have a form ancestor.');
+            }
+        } while ('form' !== $formNode->nodeName);
+
+        return $formNode;
+    }
+
+    /**
+     * Gets the position of the field node among elements with the same name
+     *
+     * BrowserKit uses the field name as index to find the field in its Form object.
+     * When multiple fields have the same name (checkboxes for instance), it will return
+     * an array of elements in the order they appear in the DOM.
+     *
+     * @param \DOMElement $fieldNode
+     *
+     * @return integer
+     */
+    private function getFieldPosition(\DOMElement $fieldNode)
+    {
+        $elements = $this->getCrawler()->filterXPath('//*[@name=\''.$fieldNode->getAttribute('name').'\']');
+
+        if (count($elements) > 1) {
+            // more than one element contains this name !
+            // so we need to find the position of $fieldNode
+            foreach ($elements as $key => $element) {
+                /** @var \DOMElement $element */
+                if ($element->getNodePath() === $fieldNode->getNodePath()) {
+                    return $key;
+                }
+            }
+        }
+
+        return 0;
+    }
+
+    private function submit(Form $form)
+    {
+        $formId = $this->getFormNodeId($form->getFormNode());
+
+        if (isset($this->forms[$formId])) {
+            $this->mergeForms($form, $this->forms[$formId]);
+        }
+
+        // remove empty file fields from request
+        foreach ($form->getFiles() as $name => $field) {
+            if (empty($field['name']) && empty($field['tmp_name'])) {
+                $form->remove($name);
+            }
+        }
+
+        foreach ($form->all() as $field) {
+            // Add a fix for https://github.com/symfony/symfony/pull/10733 to support Symfony versions which are not fixed
+            if ($field instanceof TextareaFormField && null === $field->getValue()) {
+                $field->setValue('');
+            }
+        }
+
+        $this->client->submit($form);
+
+        $this->forms = array();
+    }
+
+    private function resetForm(\DOMElement $fieldNode)
+    {
+        $formNode = $this->getFormNode($fieldNode);
+        $formId = $this->getFormNodeId($formNode);
+        unset($this->forms[$formId]);
+    }
+
+    /**
+     * Determines if a node can submit a form.
+     *
+     * @param \DOMElement $node Node.
+     *
+     * @return boolean
+     */
+    private function canSubmitForm(\DOMElement $node)
+    {
+        $type = $node->hasAttribute('type') ? $node->getAttribute('type') : null;
+
+        if ('input' == $node->nodeName && in_array($type, array('submit', 'image'))) {
+            return true;
+        }
+
+        return 'button' == $node->nodeName && (null === $type || 'submit' == $type);
+    }
+
+    /**
+     * Determines if a node can reset a form.
+     *
+     * @param \DOMElement $node Node.
+     *
+     * @return boolean
+     */
+    private function canResetForm(\DOMElement $node)
+    {
+        $type = $node->hasAttribute('type') ? $node->getAttribute('type') : null;
+
+        return in_array($node->nodeName, array('input', 'button')) && 'reset' == $type;
+    }
+
+    /**
+     * Returns form node unique identifier.
+     *
+     * @param \DOMElement $form
+     *
+     * @return string
+     */
+    private function getFormNodeId(\DOMElement $form)
+    {
+        return md5($form->getLineNo() . $form->getNodePath() . $form->nodeValue);
+    }
+
+    /**
+     * Gets the value of an option element
+     *
+     * @param \DOMElement $option
+     *
+     * @return string
+     *
+     * @see \Symfony\Component\DomCrawler\Field\ChoiceFormField::buildOptionValue
+     */
+    private function getOptionValue(\DOMElement $option)
+    {
+        if ($option->hasAttribute('value')) {
+            return $option->getAttribute('value');
+        }
+
+        if (!empty($option->nodeValue)) {
+            return $option->nodeValue;
+        }
+
+        return '1'; // DomCrawler uses 1 by default if there is no text in the option
+    }
+
+    /**
+     * Merges second form values into first one.
+     *
+     * @param Form $to   merging target
+     * @param Form $from merging source
+     */
+    private function mergeForms(Form $to, Form $from)
+    {
+        foreach ($from->all() as $name => $field) {
+            $fieldReflection = new \ReflectionObject($field);
+            $nodeReflection  = $fieldReflection->getProperty('node');
+            $valueReflection = $fieldReflection->getProperty('value');
+
+            $nodeReflection->setAccessible(true);
+            $valueReflection->setAccessible(true);
+
+            if (!($field instanceof InputFormField && in_array(
+                $nodeReflection->getValue($field)->getAttribute('type'),
+                array('submit', 'button', 'image')
+            ))) {
+                $valueReflection->setValue($to[$name], $valueReflection->getValue($field));
+            }
+        }
+    }
+
+    /**
+     * Returns DOMElement from crawler instance.
+     *
+     * @param Crawler $crawler
+     *
+     * @return \DOMElement
+     *
+     * @throws DriverException when the node does not exist
+     */
+    private function getCrawlerNode(Crawler $crawler)
+    {
+        $crawler->rewind();
+        $node = $crawler->current();
+
+        if (null !== $node) {
+            return $node;
+        }
+
+        throw new DriverException('The element does not exist');
+    }
+
+    /**
+     * Returns a crawler filtered for the given XPath, requiring at least 1 result.
+     *
+     * @param string $xpath
+     *
+     * @return Crawler
+     *
+     * @throws DriverException when no matching elements are found
+     */
+    private function getFilteredCrawler($xpath)
+    {
+        if (!count($crawler = $this->getCrawler()->filterXPath($xpath))) {
+            throw new DriverException(sprintf('There is no element matching XPath "%s"', $xpath));
+        }
+
+        return $crawler;
+    }
+
+    /**
+     * Returns crawler instance (got from client).
+     *
+     * @return Crawler
+     *
+     * @throws DriverException
+     */
+    private function getCrawler()
+    {
+        $crawler = $this->client->getCrawler();
+
+        if (null === $crawler) {
+            throw new DriverException('Unable to access the response content before visiting a page');
+        }
+
+        return $crawler;
+    }
+}
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/BrowserKitConfig.php b/core/vendor/behat/mink-browserkit-driver/tests/BrowserKitConfig.php
new file mode 100644
index 0000000..b80a2fb
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/BrowserKitConfig.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver;
+
+use Behat\Mink\Driver\BrowserKitDriver;
+use Symfony\Component\HttpKernel\Client;
+
+class BrowserKitConfig extends AbstractConfig
+{
+    public static function getInstance()
+    {
+        return new self();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDriver()
+    {
+        $client = new Client(require(__DIR__.'/app.php'));
+
+        return new BrowserKitDriver($client);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getWebFixturesUrl()
+    {
+        return 'http://localhost';
+    }
+
+    protected function supportsJs()
+    {
+        return false;
+    }
+}
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/Custom/BaseUrlTest.php b/core/vendor/behat/mink-browserkit-driver/tests/Custom/BaseUrlTest.php
new file mode 100644
index 0000000..779426e
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/Custom/BaseUrlTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Custom;
+
+use Behat\Mink\Driver\BrowserKitDriver;
+use Behat\Mink\Session;
+use Symfony\Component\HttpKernel\Client;
+
+/**
+ * @group functional
+ */
+class BaseUrlTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseUrl()
+    {
+        $client = new Client(require(__DIR__.'/../app.php'));
+        $driver = new BrowserKitDriver($client, 'http://localhost/foo/');
+        $session = new Session($driver);
+
+        $session->visit('http://localhost/foo/index.html');
+        $this->assertEquals(200, $session->getStatusCode());
+        $this->assertEquals('http://localhost/foo/index.html', $session->getCurrentUrl());
+    }
+}
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/Custom/ErrorHandlingTest.php b/core/vendor/behat/mink-browserkit-driver/tests/Custom/ErrorHandlingTest.php
new file mode 100644
index 0000000..32335c5
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/Custom/ErrorHandlingTest.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Custom;
+
+use Behat\Mink\Driver\BrowserKitDriver;
+use Symfony\Component\BrowserKit\Client;
+use Symfony\Component\BrowserKit\Response;
+
+class ErrorHandlingTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var TestClient
+     */
+    private $client;
+
+    protected function setUp()
+    {
+        $this->client = new TestClient();
+    }
+
+    public function testGetClient()
+    {
+        $this->assertSame($this->client, $this->getDriver()->getClient());
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\DriverException
+     * @expectedExceptionMessage Unable to access the response before visiting a page
+     */
+    public function testGetResponseHeaderWithoutVisit()
+    {
+        $this->getDriver()->getResponseHeaders();
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\DriverException
+     * @expectedExceptionMessage Unable to access the response content before visiting a page
+     */
+    public function testFindWithoutVisit()
+    {
+        $this->getDriver()->find('//html');
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\DriverException
+     * @expectedExceptionMessage Unable to access the request before visiting a page
+     */
+    public function testGetCurrentUrlWithoutVisit()
+    {
+        $this->getDriver()->getCurrentUrl();
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\DriverException
+     * @expectedExceptionMessage The selected node has an invalid form attribute (foo)
+     */
+    public function testNotMatchingHtml5FormId()
+    {
+        $html = <<<'HTML'
+<html>
+<body>
+    <form id="test">
+        <input name="test" value="foo" form="foo">
+        <input type="submit">
+    </form>
+</body>
+</html>
+HTML;
+
+        $this->client->setNextResponse(new Response($html));
+
+        $driver = $this->getDriver();
+        $driver->visit('/index.php');
+        $driver->setValue('//input[./@name="test"]', 'bar');
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\DriverException
+     * @expectedExceptionMessage The selected node has an invalid form attribute (foo)
+     */
+    public function testInvalidHtml5FormId()
+    {
+        $html = <<<'HTML'
+<html>
+<body>
+    <form id="test">
+        <input name="test" value="foo" form="foo">
+        <input type="submit">
+    </form>
+    <div id="foo"></div>
+</body>
+</html>
+HTML;
+
+        $this->client->setNextResponse(new Response($html));
+
+        $driver = $this->getDriver();
+        $driver->visit('/index.php');
+        $driver->setValue('//input[./@name="test"]', 'bar');
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\DriverException
+     * @expectedExceptionMessage The selected node does not have a form ancestor.
+     */
+    public function testManipulateInputWithoutForm()
+    {
+        $html = <<<'HTML'
+<html>
+<body>
+    <form id="test">
+        <input type="submit">
+    </form>
+    <div id="foo">
+        <input name="test" value="foo">
+    </div>
+</body>
+</html>
+HTML;
+
+        $this->client->setNextResponse(new Response($html));
+
+        $driver = $this->getDriver();
+        $driver->visit('/index.php');
+        $driver->setValue('//input[./@name="test"]', 'bar');
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\DriverException
+     * @expectedExceptionMessage Behat\Mink\Driver\BrowserKitDriver supports clicking on links and buttons only. But "div" provided
+     */
+    public function testClickOnUnsupportedElement()
+    {
+        $html = <<<'HTML'
+<html>
+<body>
+    <div></div>
+</body>
+</html>
+HTML;
+
+        $this->client->setNextResponse(new Response($html));
+
+        $driver = $this->getDriver();
+        $driver->visit('/index.php');
+        $driver->click('//div');
+    }
+
+    private function getDriver()
+    {
+        return new BrowserKitDriver($this->client);
+    }
+}
+
+class TestClient extends Client
+{
+    protected $nextResponse = null;
+    protected $nextScript = null;
+
+    public function setNextResponse(Response $response)
+    {
+        $this->nextResponse = $response;
+    }
+
+    public function setNextScript($script)
+    {
+        $this->nextScript = $script;
+    }
+
+    protected function doRequest($request)
+    {
+        if (null === $this->nextResponse) {
+            return new Response();
+        }
+
+        $response = $this->nextResponse;
+        $this->nextResponse = null;
+
+        return $response;
+    }
+}
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/app.php b/core/vendor/behat/mink-browserkit-driver/tests/app.php
new file mode 100644
index 0000000..d3f1236
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/app.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace app;
+
+$app = new \Silex\Application();
+$app->register(new \Silex\Provider\SessionServiceProvider());
+
+$def = realpath(__DIR__.'/../vendor/behat/mink/driver-testsuite/web-fixtures');
+$ovr = realpath(__DIR__.'/web-fixtures');
+$cbk = function ($file) use ($app, $def, $ovr) {
+    $file = str_replace('.file', '.php', $file);
+    $path = file_exists($ovr.'/'.$file) ? $ovr.'/'.$file : $def.'/'.$file;
+    $resp = null;
+
+    ob_start();
+    include($path);
+    $content = ob_get_clean();
+
+    if ($resp) {
+        if ('' === $resp->getContent()) {
+            $resp->setContent($content);
+        }
+
+        return $resp;
+    }
+
+    return $content;
+};
+
+$app->get('/{file}', $cbk)->assert('file', '.*');
+$app->post('/{file}', $cbk)->assert('file', '.*');
+
+$app['debug'] = true;
+$app['exception_handler']->disable();
+$app['session.test'] = true;
+
+return $app;
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/404.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/404.php
new file mode 100644
index 0000000..2ae1ff9
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/404.php
@@ -0,0 +1,3 @@
+<?php
+
+$resp = new Symfony\Component\HttpFoundation\Response('Sorry, page not found', 404);
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/500.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/500.php
new file mode 100644
index 0000000..8f1ebb3
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/500.php
@@ -0,0 +1,3 @@
+<?php
+
+$resp = new Symfony\Component\HttpFoundation\Response('Sorry, a server error happened', 500);
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/advanced_form_post.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/advanced_form_post.php
new file mode 100644
index 0000000..71d1c8d
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/advanced_form_post.php
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Advanced form save</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+</head>
+<body>
+<?php
+error_reporting(0);
+
+$request = $app['request'];
+$POST = $request->request->all();
+$FILES = $request->files->all();
+
+if (isset($POST['select_multiple_numbers']) && false !== strpos($POST['select_multiple_numbers'][0], ',')) {
+    $POST['select_multiple_numbers'] = explode(',', $POST['select_multiple_numbers'][0]);
+}
+
+// checkbox can have any value and will be successful in case "on"
+// http://www.w3.org/TR/html401/interact/forms.html#checkbox
+$POST['agreement'] = isset($POST['agreement']) ? 'on' : 'off';
+ksort($POST);
+echo str_replace('>', '', var_export($POST, true)) . "\n";
+if (isset($FILES['about']) && file_exists($FILES['about']->getPathname())) {
+    echo $FILES['about']->getClientOriginalName() . "\n";
+    echo file_get_contents($FILES['about']->getPathname());
+} else {
+    echo "no file";
+}
+?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_auth.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_auth.php
new file mode 100644
index 0000000..48132b6
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_auth.php
@@ -0,0 +1,15 @@
+<?php
+$SERVER = $app['request']->server->all();
+
+$username = isset($SERVER['PHP_AUTH_USER']) ? $SERVER['PHP_AUTH_USER'] : false;
+$password = isset($SERVER['PHP_AUTH_PW']) ? $SERVER['PHP_AUTH_PW'] : false;
+
+if ($username == 'mink-user' && $password == 'mink-password') {
+    echo 'is authenticated';
+} else {
+    $resp = new \Symfony\Component\HttpFoundation\Response();
+    $resp->setStatusCode(401);
+    $resp->headers->set('WWW-Authenticate', 'Basic realm="Mink Testing Area"');
+
+    echo 'is not authenticated';
+}
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_form_post.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_form_post.php
new file mode 100644
index 0000000..1efe45e
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_form_post.php
@@ -0,0 +1,14 @@
+<?php  ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Basic Form Saving</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Anket for <?php echo $app['request']->request->get('first_name') ?></h1>
+
+    <span id="first">Firstname: <?php echo $app['request']->request->get('first_name') ?></span>
+    <span id="last">Lastname: <?php echo $app['request']->request->get('last_name') ?></span>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_get_form.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_get_form.php
new file mode 100644
index 0000000..fd2817d
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_get_form.php
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Basic Get Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Basic Get Form Page</h1>
+
+    <div id="serach">
+        <?php
+        $GET = $app['request']->query->all();
+        echo isset($GET['q']) && $GET['q'] ? $GET['q'] : 'No search query'
+        ?>
+    </div>
+
+    <form>
+        <input name="q" value="" type="text" />
+
+        <input type="submit" value="Find" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page1.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page1.php
new file mode 100644
index 0000000..a928b2f
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page1.php
@@ -0,0 +1,17 @@
+<?php
+    $resp = new Symfony\Component\HttpFoundation\Response();
+    $cook = new Symfony\Component\HttpFoundation\Cookie('srvr_cookie', 'srv_var_is_set', 0, '/');
+    $resp->headers->setCookie($cook);
+?>
+<!doctype html public "-//w3c//dtd xhtml 1.1//en" "http://www.w3.org/tr/xhtml11/dtd/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>basic form</title>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
+    <script>
+    </script>
+</head>
+<body>
+    basic page with cookie set from server side
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page2.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page2.php
new file mode 100644
index 0000000..ab54243
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page2.php
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Basic Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+    <script>
+    </script>
+</head>
+<body>
+    Previous cookie: <?php
+        echo $app['request']->cookies->has('srvr_cookie') ? $app['request']->cookies->get('srvr_cookie') : 'NO';
+    ?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page3.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page3.php
new file mode 100644
index 0000000..f24d587
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page3.php
@@ -0,0 +1,20 @@
+<?php
+
+$hasCookie = $app['request']->cookies->has('foo');
+$resp = new Symfony\Component\HttpFoundation\Response();
+$cook = new Symfony\Component\HttpFoundation\Cookie('foo', 'bar');
+$resp->headers->setCookie($cook);
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>HttpOnly Cookie Test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+    <script>
+    </script>
+</head>
+<body>
+    <div id="cookie-status">Has Cookie: <?php echo json_encode($hasCookie) ?></div>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/headers.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/headers.php
new file mode 100644
index 0000000..b829425
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/headers.php
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Headers page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <?php print_r($app['request']->server->all()); ?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue130.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue130.php
new file mode 100644
index 0000000..2079673
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue130.php
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<html>
+<body>
+    <?php
+    if ('1' === $app['request']->query->get('p')) {
+        echo '<a href="/issue130.php?p=2">Go to 2</a>';
+    } else {
+        echo '<strong>'.$app['request']->headers->get('referer').'</strong>';
+    }
+    ?>
+</body>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue140.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue140.php
new file mode 100644
index 0000000..42d8437
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue140.php
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<html>
+<body>
+<?php if ($app['request']->isMethod('POST')) {
+    $resp = new Symfony\Component\HttpFoundation\Response();
+    $cook = new Symfony\Component\HttpFoundation\Cookie('tc', $app['request']->request->get('cookie_value'));
+    $resp->headers->setCookie($cook);
+} elseif ($app['request']->query->has('show_value')) {
+    echo $app['request']->cookies->get('tc');
+    return;
+}
+?>
+    <form method="post">
+        <input name="cookie_value">
+        <input type="submit" value="Set cookie">
+    </form>
+</body>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/print_cookies.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/print_cookies.php
new file mode 100644
index 0000000..ac6f078
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/print_cookies.php
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Cookies page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <?php
+    $cookies = $app['request']->cookies->all();
+    unset($cookies['MOCKSESSID']);
+
+    if (isset($cookies['srvr_cookie'])) {
+        $srvrCookie = $cookies['srvr_cookie'];
+        unset($cookies['srvr_cookie']);
+        $cookies['_SESS'] = '';
+        $cookies['srvr_cookie'] = $srvrCookie;
+    }
+
+    foreach ($cookies as $name => $val) {
+        $cookies[$name] = (string)$val;
+    }
+    echo str_replace(array('>'), '', var_export($cookies, true));
+    ?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/redirector.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/redirector.php
new file mode 100644
index 0000000..39e8a53
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/redirector.php
@@ -0,0 +1,3 @@
+<?php
+
+$resp = new Symfony\Component\HttpFoundation\RedirectResponse('/redirect_destination.html');
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/response_headers.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/response_headers.php
new file mode 100644
index 0000000..a2f48f8
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/response_headers.php
@@ -0,0 +1,15 @@
+<?php
+    $resp = new Symfony\Component\HttpFoundation\Response();
+    $resp->headers->set('X-Mink-Test', 'response-headers');
+?>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Response headers</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+    <h1>Response headers</h1>
+</body>
+</html>
+
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/session_test.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/session_test.php
new file mode 100644
index 0000000..58576e3
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/session_test.php
@@ -0,0 +1,19 @@
+<?php
+$session = $app['request']->getSession();
+
+if ($app['request']->query->has('login')) {
+    $session->migrate();
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Session Test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+    <script>
+    </script>
+</head>
+<body>
+    <div id="session-id"><?php echo $session->getId() ?></div>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page1.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page1.php
new file mode 100644
index 0000000..807c23e
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page1.php
@@ -0,0 +1,18 @@
+<?php
+    $requestUri = $app['request']->server->get('REQUEST_URI');
+    $resp = new Symfony\Component\HttpFoundation\Response();
+    $cook = new Symfony\Component\HttpFoundation\Cookie('srvr_cookie', 'srv_var_is_set_sub_folder', 0, dirname($requestUri));
+    $resp->headers->setCookie($cook);
+?>
+<!doctype html public "-//w3c//dtd xhtml 1.1//en" "http://www.w3.org/tr/xhtml11/dtd/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>basic form</title>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
+    <script>
+    </script>
+</head>
+<body>
+    basic page with cookie set from server side
+</body>
+</html>
diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page2.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page2.php
new file mode 100644
index 0000000..22a7dab
--- /dev/null
+++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page2.php
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
+<head>
+    <title>Basic Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+    <script>
+    </script>
+</head>
+<body>
+    Previous cookie: <?php
+    if ($app['request']->cookies->has('srvr_cookie')) {
+        echo $app['request']->cookies->get('srvr_cookie');
+    } else {
+        echo 'NO';
+    }
+    ?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink-goutte-driver/.travis.yml b/core/vendor/behat/mink-goutte-driver/.travis.yml
new file mode 100644
index 0000000..b09e457
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/.travis.yml
@@ -0,0 +1,19 @@
+language: php
+
+php: [5.3, 5.4, 5.5, 5.6, hhvm]
+
+before_script:
+  - export WEB_FIXTURES_HOST=http://localhost
+
+  - composer install --dev --prefer-source
+
+  - sudo apt-get update
+  - sudo apt-get install -y --force-yes apache2 libapache2-mod-php5
+  - sudo sed -i -e "s,/var/www,$(pwd)/vendor/behat/mink/driver-testsuite/web-fixtures,g" /etc/apache2/sites-available/default
+  - sudo /etc/init.d/apache2 restart
+
+script: phpunit -v --coverage-clover=coverage.clover
+
+after_script:
+  - wget https://scrutinizer-ci.com/ocular.phar
+  - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
diff --git a/core/vendor/behat/mink-goutte-driver/CHANGELOG.md b/core/vendor/behat/mink-goutte-driver/CHANGELOG.md
new file mode 100644
index 0000000..fcaa7f0
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/CHANGELOG.md
@@ -0,0 +1,20 @@
+1.1.0 / 2014-10-09
+==================
+
+The driver now relies on BrowserKitDriver 1.2.x, so all changes of this driver are relevant.
+The changes below only describe the changes related to GoutteDriver specifically.
+
+New features:
+
+* Added the possibility to use a normal Goutte client instead of requiring to use an extended one
+* Added the support of Goutte 2.0 as well
+
+Bug fixes:
+
+* Fixed the support of disabling basic auth
+* Fixed the resetting of the driver to reset the basic auth
+
+Testing:
+
+* Updated the testsuite to use the new Mink 1.6 driver testsuite
+* Added testing on HHVM
diff --git a/core/vendor/behat/mink-goutte-driver/LICENSE b/core/vendor/behat/mink-goutte-driver/LICENSE
new file mode 100644
index 0000000..3365ae6
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012-2013 Konstantin Kudryashov <ever.zet@gmail.com>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/core/vendor/behat/mink-goutte-driver/README.md b/core/vendor/behat/mink-goutte-driver/README.md
new file mode 100644
index 0000000..0aac57f
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/README.md
@@ -0,0 +1,60 @@
+Mink Goutte Driver
+==================
+
+[![Latest Stable Version](https://poser.pugx.org/behat/mink-goutte-driver/v/stable.svg)](https://packagist.org/packages/behat/mink-goutte-driver)
+[![Latest Unstable Version](https://poser.pugx.org/behat/mink-goutte-driver/v/unstable.svg)](https://packagist.org/packages/behat/mink-goutte-driver)
+[![Total Downloads](https://poser.pugx.org/behat/mink-goutte-driver/downloads.svg)](https://packagist.org/packages/behat/mink-goutte-driver)
+[![Build Status](https://travis-ci.org/Behat/MinkGoutteDriver.svg?branch=master)](https://travis-ci.org/Behat/MinkGoutteDriver)
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/badges/quality-score.png?s=ca141bb2cad18e74cf3d3b132b1a6aa0f3f004a5)](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/)
+[![Code Coverage](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/badges/coverage.png?s=ca2d17a948660bfaeb4a95bf1a709644305c54f3)](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/)
+[![License](https://poser.pugx.org/behat/mink-goutte-driver/license.svg)](https://packagist.org/packages/behat/mink-goutte-driver)
+
+Usage Example
+-------------
+
+``` php
+<?php
+
+require "vendor/autoload.php";
+
+use Behat\Mink\Mink,
+    Behat\Mink\Session,
+    Behat\Mink\Driver\GoutteDriver,
+    Behat\Mink\Driver\Goutte\Client as GoutteClient;
+
+$mink = new Mink(array(
+    'goutte' => new Session(new GoutteDriver(new GoutteClient())),
+));
+
+$session = $mink->getSession('goutte');
+$session->visit("http://php.net/");
+$session->getPage()->clickLink('Downloads');
+echo $session->getCurrentUrl() . PHP_EOL;
+```
+
+Installation
+------------
+
+Add a file composer.json with content:
+
+``` json
+{
+    "require": {
+        "behat/mink":               "~1.5",
+        "behat/mink-goutte-driver": "~1.0"
+    }
+}
+```
+
+(or merge the above into your project's existing composer.json file)
+
+``` bash
+$> curl -sS https://getcomposer.org/installer | php
+$> php composer.phar install
+```
+
+Maintainers
+-----------
+
+* Konstantin Kudryashov [everzet](http://github.com/everzet)
+* Other [awesome developers](https://github.com/Behat/MinkGoutteDriver/graphs/contributors)
diff --git a/core/vendor/behat/mink-goutte-driver/composer.json b/core/vendor/behat/mink-goutte-driver/composer.json
new file mode 100644
index 0000000..7444a50
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/composer.json
@@ -0,0 +1,41 @@
+{
+    "name":         "behat/mink-goutte-driver",
+    "description":  "Goutte driver for Mink framework",
+    "keywords":     ["goutte", "headless", "testing", "browser"],
+    "homepage":     "http://mink.behat.org/",
+    "type":         "mink-driver",
+    "license":      "MIT",
+
+    "authors": [
+        {
+            "name":      "Konstantin Kudryashov",
+            "email":     "ever.zet@gmail.com",
+            "homepage":  "http://everzet.com"
+        }
+    ],
+
+    "require": {
+        "php":                           ">=5.3.1",
+        "behat/mink":                    "~1.6@dev",
+        "behat/mink-browserkit-driver":  "~1.2@dev",
+        "fabpot/goutte":                 "~1.0.4|~2.0"
+    },
+
+    "autoload": {
+        "psr-0": {
+            "Behat\\Mink\\Driver": "src/"
+        }
+    },
+
+    "autoload-dev": {
+        "psr-4": {
+            "Behat\\Mink\\Tests\\Driver\\": "tests"
+        }
+    },
+
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.1.x-dev"
+        }
+    }
+}
diff --git a/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist b/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist
new file mode 100644
index 0000000..f374c53
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit colors="true" bootstrap="vendor/behat/mink/driver-testsuite/bootstrap.php">
+    <testsuites>
+        <testsuite name="Driver test suite">
+            <directory>tests</directory>
+            <directory>vendor/behat/mink/driver-testsuite/tests/Basic</directory>
+            <directory>vendor/behat/mink/driver-testsuite/tests/Form</directory>
+        </testsuite>
+    </testsuites>
+
+    <php>
+        <var name="driver_config_factory" value="Behat\Mink\Tests\Driver\GoutteConfig::getInstance" />
+
+        <!--server name="WEB_FIXTURES_HOST" value="http://test.mink.dev" /-->
+    </php>
+
+    <filter>
+        <whitelist>
+            <directory>./src/Behat/Mink/Driver</directory>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php
new file mode 100644
index 0000000..9d64be7
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Behat\Mink.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Driver\Goutte;
+
+use Goutte\Client as BaseClient;
+use Symfony\Component\BrowserKit\Response;
+
+/**
+ * Client overrides to support Mink functionality.
+ */
+class Client extends BaseClient
+{
+    /**
+     * Reads response meta tags to guess content-type charset.
+     *
+     * @param Response $response
+     *
+     * @return Response
+     */
+    protected function filterResponse($response)
+    {
+        $contentType = $response->getHeader('Content-Type');
+
+        if (!$contentType || false === strpos($contentType, 'charset=')) {
+            if (preg_match('/\<meta[^\>]+charset *= *["\']?([a-zA-Z\-0-9]+)/i', $response->getContent(), $matches)) {
+                $headers = $response->getHeaders();
+                $headers['Content-Type'] = $contentType.';charset='.$matches[1];
+
+                $response = new Response($response->getContent(), $response->getStatus(), $headers);
+            }
+        }
+
+        return parent::filterResponse($response);
+    }
+}
diff --git a/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php
new file mode 100644
index 0000000..a42d5a2
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php
@@ -0,0 +1,75 @@
+<?php
+
+/*
+ * This file is part of the Behat\Mink.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Driver;
+
+use Behat\Mink\Driver\Goutte\Client as ExtendedClient;
+use Goutte\Client;
+
+/**
+ * Goutte driver.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class GoutteDriver extends BrowserKitDriver
+{
+    /**
+     * Initializes Goutte driver.
+     *
+     * @param Client $client Goutte client instance
+     */
+    public function __construct(Client $client = null)
+    {
+        parent::__construct($client ?: new ExtendedClient());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setBasicAuth($user, $password)
+    {
+        if (false === $user) {
+            $this->getClient()->resetAuth();
+
+            return;
+        }
+
+        $this->getClient()->setAuth($user, $password);
+    }
+
+    /**
+     * Gets the Goutte client.
+     *
+     * The method is overwritten only to provide the appropriate return type hint.
+     *
+     * @return Client
+     */
+    public function getClient()
+    {
+        return parent::getClient();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reset()
+    {
+        parent::reset();
+        $this->getClient()->resetAuth();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function prepareUrl($url)
+    {
+        return $url;
+    }
+}
diff --git a/core/vendor/behat/mink-goutte-driver/tests/Custom/InstantiationTest.php b/core/vendor/behat/mink-goutte-driver/tests/Custom/InstantiationTest.php
new file mode 100644
index 0000000..db75a18
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/tests/Custom/InstantiationTest.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Custom;
+
+use Behat\Mink\Driver\GoutteDriver;
+
+class InstantiationTest extends \PHPUnit_Framework_TestCase
+{
+    public function testInstantiateWithClient()
+    {
+        $client = $this->getMockBuilder('Goutte\Client')->disableOriginalConstructor()->getMock();
+        $client->expects($this->once())
+            ->method('followRedirects')
+            ->with(true);
+
+        $driver = new GoutteDriver($client);
+
+        $this->assertSame($client, $driver->getClient());
+    }
+
+    public function testInstantiateWithoutClient()
+    {
+        $driver = new GoutteDriver();
+
+        $this->assertInstanceOf('Behat\Mink\Driver\Goutte\Client', $driver->getClient());
+    }
+}
diff --git a/core/vendor/behat/mink-goutte-driver/tests/GoutteConfig.php b/core/vendor/behat/mink-goutte-driver/tests/GoutteConfig.php
new file mode 100644
index 0000000..bbe90f7
--- /dev/null
+++ b/core/vendor/behat/mink-goutte-driver/tests/GoutteConfig.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver;
+
+use Behat\Mink\Driver\GoutteDriver;
+
+class GoutteConfig extends AbstractConfig
+{
+    public static function getInstance()
+    {
+        return new self();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDriver()
+    {
+        return new GoutteDriver();
+    }
+
+    protected function supportsJs()
+    {
+        return false;
+    }
+}
diff --git a/core/vendor/behat/mink/.travis.yml b/core/vendor/behat/mink/.travis.yml
new file mode 100644
index 0000000..74ea58c
--- /dev/null
+++ b/core/vendor/behat/mink/.travis.yml
@@ -0,0 +1,12 @@
+language: php
+
+php: [5.3, 5.4, 5.5, 5.6, hhvm]
+
+before_script:
+  - composer install --dev --prefer-source
+
+script: phpunit -v --coverage-clover=coverage.clover
+
+after_script:
+  - wget https://scrutinizer-ci.com/ocular.phar
+  - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
diff --git a/core/vendor/behat/mink/CHANGES.md b/core/vendor/behat/mink/CHANGES.md
new file mode 100644
index 0000000..3404cad
--- /dev/null
+++ b/core/vendor/behat/mink/CHANGES.md
@@ -0,0 +1,238 @@
+1.6.0 / 2014-09-26
+==================
+
+  * [BC break] Changed the named selector to prefer exact matches over partial matches
+  * [BC break] Changed `NodeElement::getValue` for checkboxes to return the value rather than the checked state (use `isChecked` for that)
+  * Fixed the XPath prefixing when searching inside an existing element
+  * Refactored the driver testsuite entirely and expand it to cover drivers entirely (covering many more cases for consistency)
+  * Changed `NodeElement::setValue` to support any fields rather than only input elements
+  * Removed the wrapping of any driver-level exception in a MinkException on invalid usage as it was making the code too complex
+  * Fixed the matching of the input type in the named selector to be case insensitive according to the HTML spec
+  * Introduced `Behat\Mink\Selector\Xpath\Escaper` to allow reusing the XPath escaping
+  * Deprecated `Element::getSession`. Code needing the session should get it from outside rather than the element
+  * Changed ElementNotFoundException to extend from ExpectationException
+  * Added `Element::getOuterHtml` to get the HTML code of the element including itself
+  * Fixed the name selectors to match on the `placeholder` only for textual inputs
+  * Enforced consistent behavior for drivers on 4xx and 5xx response to return the response rather than throwing an exception
+  * Added `Element::waitFor` to allow retrying some code until it succeeds or the timeout is reached
+  * Added `Element::isValid` to check whether an element still exists in the page
+  * Made `Session::executeScript` compatible across drivers by ensuring they all support the same syntaxes for the JS expression
+  * Made `Session::evaluateScript` compatible across drivers by ensuring they all support the same syntaxes for the JS expression
+  * Removed `hasClass` from `DocumentElement` (instead of triggering a fatal error)
+  * Added testing on HHVM to ensure consistency
+  * Fixed `NodeElement::getTagName` to ensure that the tag name is lowercase for all drivers
+  * Fixed `Element::hasAttribute` to ensure it supports attributes with an empty value
+  * Fixed the `field` selector to avoid matching inputs with the type `submit` or `reset`
+  * Changed the button XPath selection to accept `reset` buttons as well
+  * Changed `Session::wait` to return the condition value rather than nothing
+  * Added `Session::getWindowName` and `Session::getWindowNames` to get the name of the current and of all windows
+  * Added `Session::maximizeWindow` to maximize the window
+  * Added `NodeElement::isSelected` to check whether an `<option>` is selected
+  * Added `NodeElement::submitForm` to allow submitting a form without using a button
+  * Added assertions about the value of an attribute
+  * Added the anchor in the assertion on the URL in `WebAssert`
+
+1.5.0 / 2013-04-14
+==================
+
+  * Add `CoreDriver` to simplify future drivers improvements
+  * Add `Mink::isSessionStarted()` method
+  * Fix multibite string `preg_replace` bugs
+  * Fix handling of whitespaces in `WebAssert::pageText...()` methods
+
+1.4.3 / 2013-03-02
+==================
+
+  * Bump dependencies constraints
+
+1.4.2 / 2013-02-13
+==================
+
+  * Fix wrong test case to ensure that core drivers work as expected
+
+1.4.1 / 2013-02-10
+==================
+
+  * Update dependencies
+  * Add ElementException to element actions
+  * Rel attribute support for named selectors
+  * Add hasClass() helper to traversable elements
+  * Add getScreenshot() method to session
+  * Name attr support in named selector for button
+  * Fix for bunch of bugs
+
+1.4.0 / 2012-05-40
+==================
+
+  * New `Session::selectWindow()` and `Session::selectIFrame()` methods
+  * New built-in `WebAssert` class
+  * Fixed DocBlocks (autocompletion in any IDE now should just work)
+  * Moved Behat-related code into `Behat\MinkExtension`
+  * Removed PHPUnit test case class
+  * Updated composer dependencies to not require custom repository anymore
+  * All drivers moved into separate packages
+
+1.3.3 / 2012-03-23
+==================
+
+  * Prevent exceptions in `__toString()`
+  * Added couple of useful step definitions for Behat
+  * Fixed issues #168, #211, #212, #208
+  * Lot of small bug fixes and improvements
+  * Fixed dependencies and composer installation routine
+
+1.3.2 / 2011-12-21
+==================
+
+  * Fixed webdriver registration in MinkContext
+
+1.3.1 / 2011-12-21
+==================
+
+  * Fixed Composer package
+
+1.3.0 / 2011-12-21
+==================
+
+  * Brand new Selenium2Driver (webdriver session)
+  * Multiselect bugfixes
+  * ZombieDriver back in the business
+  * Composer now manages dependencies
+  * Some MinkContext steps got fixes
+  * Lots of bug fixes and cleanup
+
+1.2.0 / 2011-11-04
+==================
+
+  * Brand new SeleniumDriver (thanks @alexandresalome)
+  * Multiselect support (multiple options selection), including new Behat steps
+  * Ability to select option by it's text (in addition to value)
+  * ZombieDriver updates
+  * Use SuiteHooks to populate parameters (no need to call parent __construct anymore)
+  * Updated Goutte and all vendors
+  * Lot of bugfixes and new tests
+
+1.1.1 / 2011-08-12
+==================
+
+  * Fixed Zombie.js server termination on Linux
+  * Fixed base_url usage for external URLs
+
+1.1.0 / 2011-08-08
+==================
+
+  * Added Zombie.js driver (thanks @b00giZm)
+  * Added pt translation (thanks Daniel Gomes)
+  * Refactored MinkContext and MinkTestCase
+
+1.0.3 / 2011-08-02
+==================
+
+  * File uploads for empty fields fixed (GoutteDriver)
+  * Lazy sessions restart
+  * `show_tmp_dir` option in MinkContext
+  * Updated to stable Symfony2 components
+  * SahiClient connection limit bumped to 60 seconds
+  * Dutch language support
+
+1.0.2 / 2011-07-22
+==================
+
+  * ElementHtmlException fixed (thanks @Stof)
+
+1.0.1 / 2011-07-21
+==================
+
+  * Fixed buggy assertions in MinkContext
+
+1.0.0 / 2011-07-20
+==================
+
+  * Added missing tests for almost everything
+  * Hude speedup for SahiDriver
+  * Support for Behat 2.0 contexts
+  * Bundled PHPUnit TestCase
+  * Deep element traversing
+  * Correct behavior of getText() method
+  * New getHtml() method
+  * Basic HTTP auth support
+  * Soft and hard session resetting
+  * Cookies management
+  * Browser history interactions (reload(), back(), forward())
+  * Weaverryan'd exception messages
+  * Huge amount of bugfixes and small additions
+
+0.3.2 / 2011-06-20
+==================
+
+  * Fixed file uploads in Goutte driver
+  * Fixed setting of long texts into fields
+  * Added getPlainText() (returns text without tags and whitespaces) method to the element's API
+  * Start_url is now optional parameter
+  * Default session (if needed) name now need to be always specified by hands with setDefaultSessionName()
+  * default_driver => default_session
+  * Updated Symfony Components
+
+0.3.1 / 2011-05-17
+==================
+
+  * Small SahiClient update (it generates SID now if no provided)
+  * setActiveSessionName => setDefaultSessionName method rename
+
+0.3.0 / 2011-05-17
+==================
+
+  * Rewritten from scratch Mink drivers handler. Now it's sessions handler. And Mink now
+    sessions-centric tool. See examples in readme. Much cleaner API now.
+
+0.2.4 / 2011-05-12
+==================
+
+  * Fixed wrong url locator function
+  * Fixed wrong regex in `should see` step
+  * Fixed delimiters use in `should see` step
+  * Added url-match step for checking urls against regex
+
+0.2.3 / 2011-05-01
+==================
+
+  * Updated SahiClient with new version, which is faster and cleaner with it's exceptions
+
+0.2.2 / 2011-05-01
+==================
+
+  * Ability to use already started browser as SahiDriver aim
+  * Added japanese translation for bundled steps (thanks @hidenorigoto)
+  * 10 seconds limit for browser connection in SahiDriver
+
+0.2.1 / 2011-04-21
+==================
+
+  * Fixed some bundled step definitions
+
+0.2.0 / 2011-04-21
+==================
+
+  * Additional step definitions
+  * Support for extended drivers configuration through behat.yml environment parameters
+  * Lots of new named selectors
+  * Bug fixes
+  * Small improvements
+
+0.1.2 / 2011-04-08
+==================
+
+  * Fixed Sahi url escaping
+
+0.1.1 / 2011-04-06
+==================
+
+  * Fixed should/should_not steps
+  * Added spanish translation
+  * Fixed forms to use <base> element
+  * Fixed small UnsupportedByDriverException issue
+
+0.1.0 / 2011-04-04
+==================
+
+  * Initial release
diff --git a/core/vendor/behat/mink/CONTRIBUTING.md b/core/vendor/behat/mink/CONTRIBUTING.md
new file mode 100644
index 0000000..5db4d85
--- /dev/null
+++ b/core/vendor/behat/mink/CONTRIBUTING.md
@@ -0,0 +1,22 @@
+# Contributing
+Mink is an open source, community-driven project. If you'd like to contribute, feel free to do this, but remember to follow these few simple rules:
+
+## Submitting an issues
+- __Driver-related__ issues must be reported in the corresponding driver repository
+- A reproducible example is required for every bug report, otherwise it will most probably be __closed without warning__
+- If you are going to make a big, substantial change, let's discuss it first
+
+## Working with Pull Requests
+1. Create your feature addition or a bug fix branch based on `master` branch in your repository's fork.
+2. Make necessary changes, but __don't mix__ code reformatting with code changes on topic.
+3. Add tests for those changes (please look into `tests/` folder for some examples). This is important so we don't break it in a future version unintentionally.
+4. Commit your code, but do not mess with `CHANGES.md`.
+5. Squash your commits by topic to preserve a clean and readable log.
+6. Create Pull Request.
+
+# Running tests
+Make sure that you don't break anything with your changes by running:
+
+```bash
+$> phpunit
+```
diff --git a/core/vendor/behat/mink/LICENSE b/core/vendor/behat/mink/LICENSE
new file mode 100644
index 0000000..14f15e8
--- /dev/null
+++ b/core/vendor/behat/mink/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2011-2013 Konstantin Kudryashov <ever.zet@gmail.com>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/core/vendor/behat/mink/README.md b/core/vendor/behat/mink/README.md
new file mode 100644
index 0000000..d87cf3a
--- /dev/null
+++ b/core/vendor/behat/mink/README.md
@@ -0,0 +1,72 @@
+Mink
+====
+[![Latest Stable Version](https://poser.pugx.org/behat/mink/v/stable.svg)](https://packagist.org/packages/behat/mink)
+[![Latest Unstable Version](https://poser.pugx.org/behat/mink/v/unstable.svg)](https://packagist.org/packages/behat/mink)
+[![Total Downloads](https://poser.pugx.org/behat/mink/downloads.svg)](https://packagist.org/packages/behat/mink)
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Behat/Mink/badges/quality-score.png?s=d4faf469d6b399df121deed6070390800722ada0)](https://scrutinizer-ci.com/g/Behat/Mink/)
+[![Code Coverage](https://scrutinizer-ci.com/g/Behat/Mink/badges/coverage.png?s=88ab1cee4e131f4ef595f17ae4837001ef2aec3b)](https://scrutinizer-ci.com/g/Behat/Mink/)
+[![Build Status](https://travis-ci.org/Behat/Mink.svg?branch=master)](https://travis-ci.org/Behat/Mink)
+[![SensioLabsInsight](https://insight.sensiolabs.com/projects/5bb8fab0-978f-428a-ae23-44ee4e129fbc/mini.png)](https://insight.sensiolabs.com/projects/5bb8fab0-978f-428a-ae23-44ee4e129fbc)
+[![License](https://poser.pugx.org/behat/mink/license.svg)](https://packagist.org/packages/behat/mink)
+
+
+Useful Links
+------------
+
+- The main website with documentation is at [http://mink.behat.org](http://mink.behat.org)
+- Official Google Group is at [http://groups.google.com/group/behat](http://groups.google.com/group/behat)
+- IRC channel on [#freenode](http://freenode.net/) is `#behat`
+- [Note on Patches/Pull Requests](CONTRIBUTING.md)
+
+Usage Example
+-------------
+
+``` php
+<?php
+
+use Behat\Mink\Mink,
+    Behat\Mink\Session,
+    Behat\Mink\Driver\GoutteDriver,
+    Behat\Mink\Driver\Goutte\Client as GoutteClient;
+
+$startUrl = 'http://example.com';
+
+// init Mink and register sessions
+$mink = new Mink(array(
+    'goutte1' => new Session(new GoutteDriver(new GoutteClient())),
+    'goutte2' => new Session(new GoutteDriver(new GoutteClient())),
+    'custom'  => new Session(new MyCustomDriver($startUrl))
+));
+
+// set the default session name
+$mink->setDefaultSessionName('goutte2');
+
+// visit a page
+$mink->getSession()->visit($startUrl);
+
+// call to getSession() without argument will always return a default session if has one (goutte2 here)
+$mink->getSession()->getPage()->findLink('Downloads')->click();
+echo $mink->getSession()->getPage()->getContent();
+
+// call to getSession() with argument will return session by its name
+$mink->getSession('custom')->getPage()->findLink('Downloads')->click();
+echo $mink->getSession('custom')->getPage()->getContent();
+
+// this all is done to make possible mixing sessions
+$mink->getSession('goutte1')->getPage()->findLink('Chat')->click();
+$mink->getSession('goutte2')->getPage()->findLink('Chat')->click();
+```
+
+Install Dependencies
+--------------------
+
+``` bash
+$> curl -sS https://getcomposer.org/installer | php
+$> php composer.phar install
+```
+
+Contributors
+------------
+
+* Konstantin Kudryashov [everzet](http://github.com/everzet) [lead developer]
+* Other [awesome developers](https://github.com/Behat/Mink/graphs/contributors)
diff --git a/core/vendor/behat/mink/composer.json b/core/vendor/behat/mink/composer.json
new file mode 100644
index 0000000..0aeee7b
--- /dev/null
+++ b/core/vendor/behat/mink/composer.json
@@ -0,0 +1,46 @@
+{
+    "name":         "behat/mink",
+    "description":  "Web acceptance testing framework for PHP 5.3",
+    "keywords":     ["web", "testing", "browser"],
+    "homepage":     "http://mink.behat.org/",
+    "type":         "library",
+    "license":      "MIT",
+
+    "authors": [
+        {
+            "name":      "Konstantin Kudryashov",
+            "email":     "ever.zet@gmail.com",
+            "homepage":  "http://everzet.com"
+        }
+    ],
+
+    "require": {
+        "php":                   ">=5.3.1",
+        "symfony/css-selector":  "~2.0"
+    },
+
+    "suggest": {
+        "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)",
+        "behat/mink-goutte-driver":     "fast headless driver for any app without JS emulation",
+        "behat/mink-selenium2-driver":  "slow, but JS-enabled driver for any app (requires Selenium2)",
+        "behat/mink-zombie-driver":     "fast and JS-enabled headless driver for any app (requires node.js)"
+    },
+
+    "autoload": {
+        "psr-0": {
+            "Behat\\Mink": "src/"
+        }
+    },
+
+    "autoload-dev": {
+        "psr-4": {
+            "Behat\\Mink\\Tests\\": "tests"
+        }
+    },
+
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.6.x-dev"
+        }
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/README.md b/core/vendor/behat/mink/driver-testsuite/README.md
new file mode 100644
index 0000000..d21a747
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/README.md
@@ -0,0 +1,93 @@
+Mink Driver testsuite
+=====================
+
+This is the common testsuite for Mink drivers to ensure consistency among implementations.
+
+Usage
+-----
+
+The testsuite of a driver should be based as follow:
+
+```json
+{
+    "require": {
+        "behat/mink": "~1.6@dev"
+    },
+
+    "autoload-dev": {
+        "psr-4": {
+            "Acme\MyDriver\Tests\\": "tests"
+        }
+    }
+}
+```
+
+```xml
+<!-- phpunit.xml.dist -->
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit colors="true" bootstrap="vendor/behat/mink/driver-testsuite/bootstrap.php">
+    <php>
+        <var name="driver_config_factory" value="Acme\MyDriver\Tests\Config::getInstance" />
+
+        <server name="WEB_FIXTURES_HOST" value="http://test.mink.dev" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Driver test suite">
+            <directory>./tests/</directory><!-- if needed to add more tests -->
+            <directory>vendor/behat/mink/driver-testsuite/tests</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./src/Acme/MyDriver</directory>
+        </whitelist>
+    </filter>
+</phpunit>
+```
+
+Then create the driver config for the testsuite:
+
+```php
+// tests/Config.php
+
+namespace Acme\MyDriver\Tests;
+
+use Behat\Mink\Tests\Driver\AbstractConfig;
+
+class Config extends AbstractConfig
+{
+    /**
+     * Creates an instance of the config.
+     *
+     * This is the callable registered as a php variable in the phpunit.xml config file.
+     * It could be outside the class but this is convenient.
+     */
+    public static function getInstance()
+    {
+        return new self();
+    }
+
+    /**
+     * Creates driver instance.
+     *
+     * @return \Behat\Mink\Driver\DriverInterface
+     */
+    public function createDriver()
+    {
+        return new \Acme\MyDriver\MyDriver();
+    }
+}
+```
+
+Some other methods are available in the AbstractConfig which can be overwritten to adapt the testsuite to
+the needs of the driver (skipping some tests for instance).
+
+Adding Driver-specific Tests
+----------------------------
+
+When adding extra test cases specific to the driver, either use your own namespace or put them in the
+``Behat\Mink\Tests\Driver\Custom`` subnamespace to ensure that you will not create conflicts with test cases
+added in the driver testsuite in the future.
diff --git a/core/vendor/behat/mink/driver-testsuite/bootstrap.php b/core/vendor/behat/mink/driver-testsuite/bootstrap.php
new file mode 100644
index 0000000..f69a45b
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/bootstrap.php
@@ -0,0 +1,28 @@
+<?php
+
+$file = __DIR__.'/../../../autoload.php';
+
+if (!file_exists($file)) {
+    echo PHP_EOL.'The Mink driver testsuite expects Mink to be installed as a composer dependency of your project'.PHP_EOL;
+    exit(1);
+}
+
+/** @var \Composer\Autoload\ClassLoader $loader */
+$loader = require $file;
+
+$loader->addPsr4('Behat\Mink\Tests\Driver\\', __DIR__.'/tests');
+
+// Clean the global variables
+unset($file);
+unset($loader);
+
+// Check the definition of the driverLoaderFactory
+
+if (!isset($GLOBALS['driver_config_factory'])) {
+    echo PHP_EOL.'The "driver_config_factory" global variable must be set.'.PHP_EOL;
+    exit(1);
+}
+if (!is_callable($GLOBALS['driver_config_factory'])) {
+    echo PHP_EOL.'The "driver_config_factory" global variable must be a callable.'.PHP_EOL;
+    exit(1);
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/AbstractConfig.php b/core/vendor/behat/mink/driver-testsuite/tests/AbstractConfig.php
new file mode 100644
index 0000000..5d02df2
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/AbstractConfig.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver;
+
+use Behat\Mink\Driver\DriverInterface;
+
+abstract class AbstractConfig
+{
+    /**
+     * Creates driver instance.
+     *
+     * @return DriverInterface
+     */
+    abstract public function createDriver();
+
+    /**
+     * Map remote file path.
+     *
+     * @param string $file File path.
+     *
+     * @return string
+     */
+    public function mapRemoteFilePath($file)
+    {
+        if (!isset($_SERVER['TEST_MACHINE_BASE_PATH']) || !isset($_SERVER['DRIVER_MACHINE_BASE_PATH'])) {
+            return $file;
+        }
+
+        $pattern = '/^' . preg_quote($_SERVER['TEST_MACHINE_BASE_PATH'], '/') . '/';
+        $basePath = $_SERVER['DRIVER_MACHINE_BASE_PATH'];
+
+        return preg_replace($pattern, $basePath, $file, 1);
+    }
+
+    /**
+     * Gets the base url to the fixture folder
+     *
+     * @return string
+     */
+    public function getWebFixturesUrl()
+    {
+        return $_SERVER['WEB_FIXTURES_HOST'];
+    }
+
+    /**
+     * @param string $testCase The name of the TestCase class
+     * @param string $test     The name of the test method
+     *
+     * @return string|null A message explaining why the test should be skipped, or null to run the test.
+     */
+    public function skipMessage($testCase, $test)
+    {
+        if (!$this->supportsCss() && 0 === strpos($testCase, 'Behat\Mink\Tests\Driver\Css\\')) {
+            return 'This driver does not support CSS.';
+        }
+
+        if (!$this->supportsJs() && 0 === strpos($testCase, 'Behat\Mink\Tests\Driver\Js\\')) {
+            return 'This driver does not support JavaScript.';
+        }
+
+        return null;
+    }
+
+    /**
+     * Whether the JS tests should run or no.
+     *
+     * @return bool
+     */
+    protected function supportsJs()
+    {
+        return true;
+    }
+
+    /**
+     * Whether the CSS tests should run or no.
+     *
+     * @return bool
+     */
+    protected function supportsCss()
+    {
+        return false;
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/BasicAuthTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/BasicAuthTest.php
new file mode 100644
index 0000000..40d932d
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/BasicAuthTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class BasicAuthTest extends TestCase
+{
+    /**
+     * @dataProvider setBasicAuthDataProvider
+     */
+    public function testSetBasicAuth($user, $pass, $pageText)
+    {
+        $session = $this->getSession();
+
+        $session->setBasicAuth($user, $pass);
+
+        $session->visit($this->pathTo('/basic_auth.php'));
+
+        $this->assertContains($pageText, $session->getPage()->getContent());
+    }
+
+    public function setBasicAuthDataProvider()
+    {
+        return array(
+            array('mink-user', 'mink-password', 'is authenticated'),
+            array('', '', 'is not authenticated'),
+        );
+    }
+
+    public function testResetBasicAuth()
+    {
+        $session = $this->getSession();
+
+        $session->setBasicAuth('mink-user', 'mink-password');
+
+        $session->visit($this->pathTo('/basic_auth.php'));
+
+        $this->assertContains('is authenticated', $session->getPage()->getContent());
+
+        $session->setBasicAuth(false);
+
+        $session->visit($this->pathTo('/headers.php'));
+
+        $this->assertNotContains('PHP_AUTH_USER', $session->getPage()->getContent());
+    }
+
+    public function testResetWithBasicAuth()
+    {
+        $session = $this->getSession();
+
+        $session->setBasicAuth('mink-user', 'mink-password');
+
+        $session->visit($this->pathTo('/basic_auth.php'));
+
+        $this->assertContains('is authenticated', $session->getPage()->getContent());
+
+        $session->reset();
+
+        $session->visit($this->pathTo('/headers.php'));
+
+        $this->assertNotContains('PHP_AUTH_USER', $session->getPage()->getContent());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/ContentTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ContentTest.php
new file mode 100644
index 0000000..e68e8d0
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ContentTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class ContentTest extends TestCase
+{
+    public function testOuterHtml()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $element = $this->getAssertSession()->elementExists('css', '.travers');
+
+        $this->assertEquals(
+            "<div class=\"travers\">\n            <div class=\"sub\">el1</div>\n".
+            "            <div class=\"sub\">el2</div>\n            <div class=\"sub\">\n".
+            "                <a href=\"some_url\">some <strong>deep</strong> url</a>\n".
+            "            </div>\n        </div>",
+            $element->getOuterHtml()
+        );
+    }
+
+    /**
+     * @dataProvider getAttributeDataProvider
+     */
+    public function testGetAttribute($attributeName, $attributeValue)
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $element = $this->getSession()->getPage()->findById('attr-elem[' . $attributeName . ']');
+
+        $this->assertNotNull($element);
+        $this->assertSame($attributeValue, $element->getAttribute($attributeName));
+    }
+
+    public function getAttributeDataProvider()
+    {
+        return array(
+            array('with-value', 'some-value'),
+            array('without-value', ''),
+            array('with-empty-value', ''),
+            array('with-missing', null),
+        );
+    }
+
+    public function testJson()
+    {
+        $this->getSession()->visit($this->pathTo('/json.php'));
+        $this->assertContains(
+            '{"key1":"val1","key2":234,"key3":[1,2,3]}',
+            $this->getSession()->getPage()->getContent()
+        );
+    }
+
+    public function testHtmlDecodingNotPerformed()
+    {
+        $session = $this->getSession();
+        $webAssert = $this->getAssertSession();
+        $session->visit($this->pathTo('/html_decoding.html'));
+        $page = $session->getPage();
+
+        $span = $webAssert->elementExists('css', 'span');
+        $input = $webAssert->elementExists('css', 'input');
+
+        $expectedHtml = '<span custom-attr="&amp;">some text</span>';
+        $this->assertContains($expectedHtml, $page->getHtml(), '.innerHTML is returned as-is');
+        $this->assertContains($expectedHtml, $page->getContent(), '.outerHTML is returned as-is');
+
+        $this->assertEquals('&', $span->getAttribute('custom-attr'), '.getAttribute value is decoded');
+        $this->assertEquals('&', $input->getAttribute('value'), '.getAttribute value is decoded');
+        $this->assertEquals('&', $input->getValue(), 'node value is decoded');
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/CookieTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/CookieTest.php
new file mode 100644
index 0000000..355dd71
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/CookieTest.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class CookieTest extends TestCase
+{
+    /**
+     * test cookie decoding
+     * @group issue140
+     */
+    public function testIssue140()
+    {
+        $this->getSession()->visit($this->pathTo('/issue140.php'));
+
+        $this->getSession()->getPage()->fillField('cookie_value', 'some:value;');
+        $this->getSession()->getPage()->pressButton('Set cookie');
+
+        $this->getSession()->visit($this->pathTo('/issue140.php?show_value'));
+        $this->assertEquals('some:value;', $this->getSession()->getCookie('tc'));
+        $this->assertEquals('some:value;', $this->getSession()->getPage()->getText());
+    }
+
+    public function testCookie()
+    {
+        $this->getSession()->visit($this->pathTo('/cookie_page2.php'));
+        $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText());
+        $this->assertNull($this->getSession()->getCookie('srvr_cookie'));
+
+        $this->getSession()->setCookie('srvr_cookie', 'client cookie set');
+        $this->getSession()->reload();
+        $this->assertContains('Previous cookie: client cookie set', $this->getSession()->getPage()->getText());
+        $this->assertEquals('client cookie set', $this->getSession()->getCookie('srvr_cookie'));
+
+        $this->getSession()->setCookie('srvr_cookie', null);
+        $this->getSession()->reload();
+        $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText());
+
+        $this->getSession()->visit($this->pathTo('/cookie_page1.php'));
+        $this->getSession()->visit($this->pathTo('/cookie_page2.php'));
+
+        $this->assertContains('Previous cookie: srv_var_is_set', $this->getSession()->getPage()->getText());
+        $this->getSession()->setCookie('srvr_cookie', null);
+        $this->getSession()->reload();
+        $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText());
+    }
+
+    /**
+     * @dataProvider cookieWithPathsDataProvider
+     */
+    public function testCookieWithPaths($cookieRemovalMode)
+    {
+        // start clean
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/sub-folder/cookie_page2.php'));
+        $this->assertContains('Previous cookie: NO', $session->getPage()->getText());
+
+        // cookie from root path is accessible in sub-folder
+        $session->visit($this->pathTo('/cookie_page1.php'));
+        $session->visit($this->pathTo('/sub-folder/cookie_page2.php'));
+        $this->assertContains('Previous cookie: srv_var_is_set', $session->getPage()->getText());
+
+        // cookie from sub-folder overrides cookie from root path
+        $session->visit($this->pathTo('/sub-folder/cookie_page1.php'));
+        $session->visit($this->pathTo('/sub-folder/cookie_page2.php'));
+        $this->assertContains('Previous cookie: srv_var_is_set_sub_folder', $session->getPage()->getText());
+
+        if ($cookieRemovalMode == 'session_reset') {
+            $session->reset();
+        } elseif ($cookieRemovalMode == 'cookie_delete') {
+            $session->setCookie('srvr_cookie', null);
+        }
+
+        // cookie is removed from all paths
+        $session->visit($this->pathTo('/sub-folder/cookie_page2.php'));
+        $this->assertContains('Previous cookie: NO', $session->getPage()->getText());
+    }
+
+    public function cookieWithPathsDataProvider()
+    {
+        return array(
+            array('session_reset'),
+            array('cookie_delete'),
+        );
+    }
+
+    public function testReset()
+    {
+        $this->getSession()->visit($this->pathTo('/cookie_page1.php'));
+        $this->getSession()->visit($this->pathTo('/cookie_page2.php'));
+        $this->assertContains('Previous cookie: srv_var_is_set', $this->getSession()->getPage()->getText());
+
+        $this->getSession()->reset();
+        $this->getSession()->visit($this->pathTo('/cookie_page2.php'));
+
+        $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText());
+
+        $this->getSession()->setCookie('srvr_cookie', 'test_cookie');
+        $this->getSession()->visit($this->pathTo('/cookie_page2.php'));
+        $this->assertContains('Previous cookie: test_cookie', $this->getSession()->getPage()->getText());
+        $this->getSession()->reset();
+        $this->getSession()->visit($this->pathTo('/cookie_page2.php'));
+        $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText());
+
+        $this->getSession()->setCookie('client_cookie1', 'some_val');
+        $this->getSession()->setCookie('client_cookie2', 123);
+        $this->getSession()->visit($this->pathTo('/session_test.php'));
+        $this->getSession()->visit($this->pathTo('/cookie_page1.php'));
+
+        $this->getSession()->visit($this->pathTo('/print_cookies.php'));
+        $this->assertContains(
+            "'client_cookie1' = 'some_val'",
+            $this->getSession()->getPage()->getText()
+        );
+        $this->assertContains(
+            "'client_cookie2' = '123'",
+            $this->getSession()->getPage()->getText()
+        );
+        $this->assertContains(
+            "_SESS' = ",
+            $this->getSession()->getPage()->getText()
+        );
+        $this->assertContains(
+            " 'srvr_cookie' = 'srv_var_is_set'",
+            $this->getSession()->getPage()->getText()
+        );
+
+        $this->getSession()->reset();
+        $this->getSession()->visit($this->pathTo('/print_cookies.php'));
+        $this->assertContains('array ( )', $this->getSession()->getPage()->getText());
+    }
+
+    public function testHttpOnlyCookieIsDeleted()
+    {
+        $this->getSession()->restart();
+        $this->getSession()->visit($this->pathTo('/cookie_page3.php'));
+        $this->assertEquals('Has Cookie: false', $this->findById('cookie-status')->getText());
+
+        $this->getSession()->reload();
+        $this->assertEquals('Has Cookie: true', $this->findById('cookie-status')->getText());
+
+        $this->getSession()->restart();
+        $this->getSession()->visit($this->pathTo('/cookie_page3.php'));
+        $this->assertEquals('Has Cookie: false', $this->findById('cookie-status')->getText());
+    }
+
+    public function testSessionPersistsBetweenRequests()
+    {
+        $this->getSession()->visit($this->pathTo('/session_test.php'));
+        $webAssert = $this->getAssertSession();
+        $node = $webAssert->elementExists('css', '#session-id');
+        $sessionId = $node->getText();
+
+        $this->getSession()->visit($this->pathTo('/session_test.php'));
+        $node = $webAssert->elementExists('css', '#session-id');
+        $this->assertEquals($sessionId, $node->getText());
+
+        $this->getSession()->visit($this->pathTo('/session_test.php?login'));
+        $node = $webAssert->elementExists('css', '#session-id');
+        $this->assertNotEquals($sessionId, $newSessionId = $node->getText());
+
+        $this->getSession()->visit($this->pathTo('/session_test.php'));
+        $node = $webAssert->elementExists('css', '#session-id');
+        $this->assertEquals($newSessionId, $node->getText());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/ErrorHandlingTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ErrorHandlingTest.php
new file mode 100644
index 0000000..440585a
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ErrorHandlingTest.php
@@ -0,0 +1,262 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class ErrorHandlingTest extends TestCase
+{
+    const NOT_FOUND_XPATH = '//html/./invalid';
+
+    const NOT_FOUND_EXCEPTION = 'Exception';
+
+    const INVALID_EXCEPTION = 'Exception';
+
+    public function testVisitErrorPage()
+    {
+        $this->getSession()->visit($this->pathTo('/500.php'));
+
+        $this->assertContains(
+            'Sorry, a server error happened',
+            $this->getSession()->getPage()->getContent(),
+            'Drivers allow loading pages with a 500 status code'
+        );
+    }
+
+    public function testCheckInvalidElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+        $element = $this->findById('user-name');
+
+        $this->setExpectedException(self::INVALID_EXCEPTION);
+        $this->getSession()->getDriver()->check($element->getXpath());
+    }
+
+    public function testCheckNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->check(self::NOT_FOUND_XPATH);
+    }
+
+    public function testUncheckInvalidElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+        $element = $this->findById('user-name');
+
+        $this->setExpectedException(self::INVALID_EXCEPTION);
+        $this->getSession()->getDriver()->uncheck($element->getXpath());
+    }
+
+    public function testUncheckNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->uncheck(self::NOT_FOUND_XPATH);
+    }
+
+    public function testSelectOptionInvalidElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+        $element = $this->findById('user-name');
+
+        $this->setExpectedException(self::INVALID_EXCEPTION);
+        $this->getSession()->getDriver()->selectOption($element->getXpath(), 'test');
+    }
+
+    public function testSelectOptionNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->selectOption(self::NOT_FOUND_XPATH, 'test');
+    }
+
+    public function testAttachFileInvalidElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+        $element = $this->findById('user-name');
+
+        $this->setExpectedException(self::INVALID_EXCEPTION);
+        $this->getSession()->getDriver()->attachFile($element->getXpath(), __FILE__);
+    }
+
+    public function testAttachFileNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->attachFile(self::NOT_FOUND_XPATH, __FILE__);
+    }
+
+    public function testSubmitFormInvalidElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+        $element = $this->findById('core');
+
+        $this->setExpectedException(self::INVALID_EXCEPTION);
+        $this->getSession()->getDriver()->submitForm($element->getXpath());
+    }
+
+    public function testSubmitFormNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->submitForm(self::NOT_FOUND_XPATH);
+    }
+
+    public function testGetTagNameNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->getTagName(self::NOT_FOUND_XPATH);
+    }
+
+    public function testGetTextNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->getText(self::NOT_FOUND_XPATH);
+    }
+
+    public function testGetHtmlNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->getHtml(self::NOT_FOUND_XPATH);
+    }
+
+    public function testGetOuterHtmlNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->getOuterHtml(self::NOT_FOUND_XPATH);
+    }
+
+    public function testGetValueNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->getValue(self::NOT_FOUND_XPATH);
+    }
+
+    public function testSetValueNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->setValue(self::NOT_FOUND_XPATH, 'test');
+    }
+
+    public function testIsSelectedNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->isSelected(self::NOT_FOUND_XPATH);
+    }
+
+    public function testIsCheckedNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->isChecked(self::NOT_FOUND_XPATH);
+    }
+
+    public function testIsVisibleNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->isVisible(self::NOT_FOUND_XPATH);
+    }
+
+    public function testClickNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->click(self::NOT_FOUND_XPATH);
+    }
+
+    public function testDoubleClickNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->doubleClick(self::NOT_FOUND_XPATH);
+    }
+
+    public function testRightClickNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->rightClick(self::NOT_FOUND_XPATH);
+    }
+
+    public function testGetAttributeNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->getAttribute(self::NOT_FOUND_XPATH, 'id');
+    }
+
+    public function testMouseOverNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->mouseOver(self::NOT_FOUND_XPATH);
+    }
+
+    public function testFocusNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->focus(self::NOT_FOUND_XPATH);
+    }
+
+    public function testBlurNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->blur(self::NOT_FOUND_XPATH);
+    }
+
+    public function testKeyPressNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->keyPress(self::NOT_FOUND_XPATH, 'a');
+    }
+
+    public function testKeyDownNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->keyDown(self::NOT_FOUND_XPATH, 'a');
+    }
+
+    public function testKeyUpNotFoundElement()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->setExpectedException(self::NOT_FOUND_EXCEPTION);
+        $this->getSession()->getDriver()->keyUp(self::NOT_FOUND_XPATH, 'a');
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/HeaderTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/HeaderTest.php
new file mode 100644
index 0000000..0288ff1
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/HeaderTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class HeaderTest extends TestCase
+{
+    /**
+     * test referrer
+     * @group issue130
+     */
+    public function testIssue130()
+    {
+        $this->getSession()->visit($this->pathTo('/issue130.php?p=1'));
+        $page = $this->getSession()->getPage();
+
+        $page->clickLink('Go to 2');
+        $this->assertEquals($this->pathTo('/issue130.php?p=1'), $page->getText());
+    }
+
+    public function testHeaders()
+    {
+        $this->getSession()->setRequestHeader('Accept-Language', 'fr');
+        $this->getSession()->visit($this->pathTo('/headers.php'));
+
+        $this->assertContains('[HTTP_ACCEPT_LANGUAGE] => fr', $this->getSession()->getPage()->getContent());
+    }
+
+    public function testSetUserAgent()
+    {
+        $session = $this->getSession();
+
+        $session->setRequestHeader('user-agent', 'foo bar');
+        $session->visit($this->pathTo('/headers.php'));
+        $this->assertContains('[HTTP_USER_AGENT] => foo bar', $session->getPage()->getContent());
+    }
+
+    public function testResetHeaders()
+    {
+        $session = $this->getSession();
+
+        $session->setRequestHeader('X-Mink-Test', 'test');
+        $session->visit($this->pathTo('/headers.php'));
+
+        $this->assertContains(
+            '[HTTP_X_MINK_TEST] => test',
+            $session->getPage()->getContent(),
+            'The custom header should be sent',
+            true
+        );
+
+        $session->reset();
+        $session->visit($this->pathTo('/headers.php'));
+
+        $this->assertNotContains(
+            '[HTTP_X_MINK_TEST] => test',
+            $session->getPage()->getContent(),
+            'The custom header should not be sent after resetting',
+            true
+        );
+    }
+
+    public function testResponseHeaders()
+    {
+        $this->getSession()->visit($this->pathTo('/response_headers.php'));
+
+        $headers = $this->getSession()->getResponseHeaders();
+
+        $lowercasedHeaders = array();
+        foreach ($headers as $name => $value) {
+            $lowercasedHeaders[str_replace('_', '-', strtolower($name))] = $value;
+        }
+
+        $this->assertArrayHasKey('x-mink-test', $lowercasedHeaders);
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/IFrameTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/IFrameTest.php
new file mode 100644
index 0000000..0ed0f9e
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/IFrameTest.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class IFrameTest extends TestCase
+{
+    public function testIFrame()
+    {
+        $this->getSession()->visit($this->pathTo('/iframe.html'));
+        $webAssert = $this->getAssertSession();
+
+        $el = $webAssert->elementExists('css', '#text');
+        $this->assertSame('Main window div text', $el->getText());
+
+        $this->getSession()->switchToIFrame('subframe');
+
+        $el = $webAssert->elementExists('css', '#text');
+        $this->assertSame('iFrame div text', $el->getText());
+
+        $this->getSession()->switchToIFrame();
+
+        $el = $webAssert->elementExists('css', '#text');
+        $this->assertSame('Main window div text', $el->getText());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/NavigationTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/NavigationTest.php
new file mode 100644
index 0000000..8ecb139
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/NavigationTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class NavigationTest extends TestCase
+{
+    public function testRedirect()
+    {
+        $this->getSession()->visit($this->pathTo('/redirector.php'));
+        $this->assertEquals($this->pathTo('/redirect_destination.html'), $this->getSession()->getCurrentUrl());
+    }
+
+    public function testPageControlls()
+    {
+        $this->getSession()->visit($this->pathTo('/randomizer.php'));
+        $number1 = $this->getSession()->getPage()->find('css', '#number')->getText();
+
+        $this->getSession()->reload();
+        $number2 = $this->getSession()->getPage()->find('css', '#number')->getText();
+
+        $this->assertNotEquals($number1, $number2);
+
+        $this->getSession()->visit($this->pathTo('/links.html'));
+        $this->getSession()->getPage()->clickLink('Random number page');
+
+        $this->assertEquals($this->pathTo('/randomizer.php'), $this->getSession()->getCurrentUrl());
+
+        $this->getSession()->back();
+        $this->assertEquals($this->pathTo('/links.html'), $this->getSession()->getCurrentUrl());
+
+        $this->getSession()->forward();
+        $this->assertEquals($this->pathTo('/randomizer.php'), $this->getSession()->getCurrentUrl());
+    }
+
+    public function testLinks()
+    {
+        $this->getSession()->visit($this->pathTo('/links.html'));
+        $page = $this->getSession()->getPage();
+        $link = $page->findLink('Redirect me to');
+
+        $this->assertNotNull($link);
+        $this->assertRegExp('/redirector\.php$/', $link->getAttribute('href'));
+        $link->click();
+
+        $this->assertEquals($this->pathTo('/redirect_destination.html'), $this->getSession()->getCurrentUrl());
+
+        $this->getSession()->visit($this->pathTo('/links.html'));
+        $page = $this->getSession()->getPage();
+        $link = $page->findLink('basic form image');
+
+        $this->assertNotNull($link);
+        $this->assertRegExp('/basic_form\.html$/', $link->getAttribute('href'));
+        $link->click();
+
+        $this->assertEquals($this->pathTo('/basic_form.html'), $this->getSession()->getCurrentUrl());
+
+        $this->getSession()->visit($this->pathTo('/links.html'));
+        $page = $this->getSession()->getPage();
+        $link = $page->findLink("Link with a ");
+
+        $this->assertNotNull($link);
+        $this->assertRegExp('/links\.html\?quoted$/', $link->getAttribute('href'));
+        $link->click();
+
+        $this->assertEquals($this->pathTo('/links.html?quoted'), $this->getSession()->getCurrentUrl());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/ScreenshotTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ScreenshotTest.php
new file mode 100644
index 0000000..329f2d3
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ScreenshotTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class ScreenshotTest extends TestCase
+{
+    public function testScreenshot()
+    {
+        if (!extension_loaded('gd')) {
+            $this->markTestSkipped('Testing screenshots requires the GD extension');
+        }
+
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $screenShot = $this->getSession()->getScreenshot();
+
+        $this->assertInternalType('string', $screenShot);
+        $this->assertFalse(base64_decode($screenShot, true), 'The returned screenshot should not be base64-encoded');
+
+        $img = imagecreatefromstring($screenShot);
+
+        if (false === $img) {
+            $this->fail('The screenshot should be a valid image');
+        }
+
+        imagedestroy($img);
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/StatusCodeTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/StatusCodeTest.php
new file mode 100644
index 0000000..34ca7f0
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/StatusCodeTest.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class StatusCodeTest extends TestCase
+{
+    public function testStatuses()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->assertEquals(200, $this->getSession()->getStatusCode());
+        $this->assertEquals($this->pathTo('/index.html'), $this->getSession()->getCurrentUrl());
+
+        $this->getSession()->visit($this->pathTo('/404.php'));
+
+        $this->assertEquals($this->pathTo('/404.php'), $this->getSession()->getCurrentUrl());
+        $this->assertEquals(404, $this->getSession()->getStatusCode());
+        $this->assertEquals('Sorry, page not found', $this->getSession()->getPage()->getContent());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/TraversingTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/TraversingTest.php
new file mode 100644
index 0000000..b31f7fc
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/TraversingTest.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class TraversingTest extends TestCase
+{
+    /**
+     * find by label
+     * @group issue211
+     */
+    public function testIssue211()
+    {
+        $this->getSession()->visit($this->pathTo('/issue211.html'));
+        $field = $this->getSession()->getPage()->findField('TÃ©lÃ©phone');
+
+        $this->assertNotNull($field);
+    }
+
+    public function testElementsTraversing()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $page = $this->getSession()->getPage();
+
+        $this->assertNotNull($page->find('css', 'h1'));
+        $this->assertEquals('Extremely useless page', $page->find('css', 'h1')->getText());
+        $this->assertEquals('h1', $page->find('css', 'h1')->getTagName());
+
+        $this->assertNotNull($page->find('xpath', '//div/strong[3]'));
+        $this->assertEquals('pariatur', $page->find('xpath', '//div/strong[3]')->getText());
+        $this->assertEquals('super-duper', $page->find('xpath', '//div/strong[3]')->getAttribute('class'));
+        $this->assertTrue($page->find('xpath', '//div/strong[3]')->hasAttribute('class'));
+
+        $this->assertNotNull($page->find('xpath', '//div/strong[2]'));
+        $this->assertEquals('veniam', $page->find('xpath', '//div/strong[2]')->getText());
+        $this->assertEquals('strong', $page->find('xpath', '//div/strong[2]')->getTagName());
+        $this->assertNull($page->find('xpath', '//div/strong[2]')->getAttribute('class'));
+        $this->assertFalse($page->find('xpath', '//div/strong[2]')->hasAttribute('class'));
+
+        $strongs = $page->findAll('css', 'div#core > strong');
+        $this->assertCount(3, $strongs);
+        $this->assertEquals('Lorem', $strongs[0]->getText());
+        $this->assertEquals('pariatur', $strongs[2]->getText());
+
+        $element = $page->find('css', '#some-element');
+
+        $this->assertEquals('some very interesting text', $element->getText());
+        $this->assertEquals(
+            "\n            some <div>very\n            </div>\n".
+            "<em>interesting</em>      text\n        ",
+            $element->getHtml()
+        );
+
+        $this->assertTrue($element->hasAttribute('data-href'));
+        $this->assertFalse($element->hasAttribute('data-url'));
+        $this->assertEquals('http://mink.behat.org', $element->getAttribute('data-href'));
+        $this->assertNull($element->getAttribute('data-url'));
+        $this->assertEquals('div', $element->getTagName());
+    }
+
+    public function testVeryDeepElementsTraversing()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $page = $this->getSession()->getPage();
+
+        $footer = $page->find('css', 'footer');
+        $this->assertNotNull($footer);
+
+        $searchForm = $footer->find('css', 'form#search-form');
+        $this->assertNotNull($searchForm);
+        $this->assertEquals('search-form', $searchForm->getAttribute('id'));
+
+        $searchInput = $searchForm->findField('Search site...');
+        $this->assertNotNull($searchInput);
+        $this->assertEquals('text', $searchInput->getAttribute('type'));
+
+        $searchInput = $searchForm->findField('Search site...');
+        $this->assertNotNull($searchInput);
+        $this->assertEquals('text', $searchInput->getAttribute('type'));
+
+        $profileForm = $footer->find('css', '#profile');
+        $this->assertNotNull($profileForm);
+
+        $profileFormDiv = $profileForm->find('css', 'div');
+        $this->assertNotNull($profileFormDiv);
+
+        $profileFormDivLabel = $profileFormDiv->find('css', 'label');
+        $this->assertNotNull($profileFormDivLabel);
+
+        $profileFormDivParent = $profileFormDivLabel->getParent();
+        $this->assertNotNull($profileFormDivParent);
+
+        $profileFormDivParent = $profileFormDivLabel->getParent();
+        $this->assertEquals('something', $profileFormDivParent->getAttribute('data-custom'));
+
+        $profileFormInput = $profileFormDivLabel->findField('user-name');
+        $this->assertNotNull($profileFormInput);
+        $this->assertEquals('username', $profileFormInput->getAttribute('name'));
+    }
+
+    public function testDeepTraversing()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $traversDiv = $this->getSession()->getPage()->findAll('css', 'div.travers');
+
+        $this->assertCount(1, $traversDiv);
+        $traversDiv = $traversDiv[0];
+
+        $subDivs = $traversDiv->findAll('css', 'div.sub');
+        $this->assertCount(3, $subDivs);
+
+        $this->assertTrue($subDivs[2]->hasLink('some deep url'));
+        $this->assertFalse($subDivs[2]->hasLink('come deep url'));
+        $subUrl = $subDivs[2]->findLink('some deep url');
+        $this->assertNotNull($subUrl);
+
+        $this->assertRegExp('/some_url$/', $subUrl->getAttribute('href'));
+        $this->assertEquals('some deep url', $subUrl->getText());
+        $this->assertEquals('some <strong>deep</strong> url', $subUrl->getHtml());
+
+        $this->assertTrue($subUrl->has('css', 'strong'));
+        $this->assertFalse($subUrl->has('css', 'em'));
+        $this->assertEquals('deep', $subUrl->find('css', 'strong')->getText());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/VisibilityTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/VisibilityTest.php
new file mode 100644
index 0000000..b3daf31
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/VisibilityTest.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Basic;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class VisibilityTest extends TestCase
+{
+    public function testVisibility()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $webAssert = $this->getAssertSession();
+
+        $clicker   = $webAssert->elementExists('css', '.elements div#clicker');
+        $invisible = $webAssert->elementExists('css', '#invisible');
+
+        $this->assertFalse($invisible->isVisible());
+        $this->assertTrue($clicker->isVisible());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Css/HoverTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Css/HoverTest.php
new file mode 100644
index 0000000..87041d4
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Css/HoverTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Css;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class HoverTest extends TestCase
+{
+    /**
+     * @group mouse-events
+     */
+    public function testMouseOverHover()
+    {
+        $this->getSession()->visit($this->pathTo('/css_mouse_events.html'));
+
+        $this->findById('reset-square')->mouseOver();
+        $this->assertActionSquareHeight(100);
+
+        $this->findById('action-square')->mouseOver();
+        $this->assertActionSquareHeight(200);
+    }
+
+    /**
+     * @group mouse-events
+     * @depends testMouseOverHover
+     */
+    public function testClickHover()
+    {
+        $this->getSession()->visit($this->pathTo('/css_mouse_events.html'));
+
+        $this->findById('reset-square')->mouseOver();
+        $this->assertActionSquareHeight(100);
+
+        $this->findById('action-square')->click();
+        $this->assertActionSquareHeight(200);
+    }
+
+    /**
+     * @group mouse-events
+     * @depends testMouseOverHover
+     */
+    public function testDoubleClickHover()
+    {
+        $this->getSession()->visit($this->pathTo('/css_mouse_events.html'));
+
+        $this->findById('reset-square')->mouseOver();
+        $this->assertActionSquareHeight(100);
+
+        $this->findById('action-square')->doubleClick();
+        $this->assertActionSquareHeight(200);
+    }
+
+    /**
+     * @group mouse-events
+     * @depends testMouseOverHover
+     */
+    public function testRightClickHover()
+    {
+        $this->getSession()->visit($this->pathTo('/css_mouse_events.html'));
+
+        $this->findById('reset-square')->mouseOver();
+        $this->assertActionSquareHeight(100);
+
+        $this->findById('action-square')->rightClick();
+        $this->assertActionSquareHeight(200);
+    }
+
+    private function assertActionSquareHeight($expected)
+    {
+        $this->assertEquals(
+            $expected,
+            $this->getSession()->evaluateScript("return window.$('#action-square').height();"),
+            'Mouse is located over the object when mouse-related action is performed'
+        );
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/CheckboxTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/CheckboxTest.php
new file mode 100644
index 0000000..e1ce4ab
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/CheckboxTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Form;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class CheckboxTest extends TestCase
+{
+    public function testManipulate()
+    {
+        $this->getSession()->visit($this->pathTo('advanced_form.html'));
+
+        $checkbox = $this->getAssertSession()->fieldExists('agreement');
+
+        $this->assertNull($checkbox->getValue());
+        $this->assertFalse($checkbox->isChecked());
+
+        $checkbox->check();
+
+        $this->assertEquals('yes', $checkbox->getValue());
+        $this->assertTrue($checkbox->isChecked());
+
+        $checkbox->uncheck();
+
+        $this->assertNull($checkbox->getValue());
+        $this->assertFalse($checkbox->isChecked());
+    }
+
+    public function testSetValue()
+    {
+        $this->getSession()->visit($this->pathTo('advanced_form.html'));
+
+        $checkbox = $this->getAssertSession()->fieldExists('agreement');
+
+        $this->assertNull($checkbox->getValue());
+        $this->assertFalse($checkbox->isChecked());
+
+        $checkbox->setValue(true);
+
+        $this->assertEquals('yes', $checkbox->getValue());
+        $this->assertTrue($checkbox->isChecked());
+
+        $checkbox->setValue(false);
+
+        $this->assertNull($checkbox->getValue());
+        $this->assertFalse($checkbox->isChecked());
+    }
+
+    public function testCheckboxMultiple()
+    {
+        $this->getSession()->visit($this->pathTo('/multicheckbox_form.html'));
+        $webAssert = $this->getAssertSession();
+
+        $this->assertEquals('Multicheckbox Test', $webAssert->elementExists('css', 'h1')->getText());
+
+        $updateMail  = $webAssert->elementExists('css', '[name="mail_types[]"][value="update"]');
+        $spamMail    = $webAssert->elementExists('css', '[name="mail_types[]"][value="spam"]');
+
+        $this->assertEquals('update', $updateMail->getValue());
+        $this->assertNull($spamMail->getValue());
+
+        $this->assertTrue($updateMail->isChecked());
+        $this->assertFalse($spamMail->isChecked());
+
+        $updateMail->uncheck();
+        $this->assertFalse($updateMail->isChecked());
+        $this->assertFalse($spamMail->isChecked());
+
+        $spamMail->check();
+        $this->assertFalse($updateMail->isChecked());
+        $this->assertTrue($spamMail->isChecked());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/GeneralTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/GeneralTest.php
new file mode 100644
index 0000000..263be70
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/GeneralTest.php
@@ -0,0 +1,312 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Form;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class GeneralTest extends TestCase
+{
+    // test multiple submit buttons
+    public function testIssue212()
+    {
+        $session = $this->getSession();
+
+        $session->visit($this->pathTo('/issue212.html'));
+
+        $field = $this->findById('poney-button');
+        $this->assertEquals('poney', $field->getValue());
+    }
+
+    public function testBasicForm()
+    {
+        $this->getSession()->visit($this->pathTo('/basic_form.html'));
+
+        $webAssert = $this->getAssertSession();
+        $page = $this->getSession()->getPage();
+        $this->assertEquals('Basic Form Page', $webAssert->elementExists('css', 'h1')->getText());
+
+        $firstname  = $webAssert->fieldExists('first_name');
+        $lastname   = $webAssert->fieldExists('lastn');
+
+        $this->assertEquals('Firstname', $firstname->getValue());
+        $this->assertEquals('Lastname', $lastname->getValue());
+
+        $firstname->setValue('Konstantin');
+        $page->fillField('last_name', 'Kudryashov');
+
+        $this->assertEquals('Konstantin', $firstname->getValue());
+        $this->assertEquals('Kudryashov', $lastname->getValue());
+
+        $page->findButton('Reset')->click();
+
+        $this->assertEquals('Firstname', $firstname->getValue());
+        $this->assertEquals('Lastname', $lastname->getValue());
+
+        $firstname->setValue('Konstantin');
+        $page->fillField('last_name', 'Kudryashov');
+
+        $page->findButton('Save')->click();
+
+        if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) {
+            $this->assertEquals('Anket for Konstantin', $webAssert->elementExists('css', 'h1')->getText());
+            $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText());
+            $this->assertEquals('Lastname: Kudryashov', $webAssert->elementExists('css', '#last')->getText());
+        }
+    }
+
+    /**
+     * @dataProvider formSubmitWaysDataProvider
+     */
+    public function testFormSubmitWays($submitVia)
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/basic_form.html'));
+        $page = $session->getPage();
+        $webAssert = $this->getAssertSession();
+
+        $firstname = $webAssert->fieldExists('first_name');
+        $firstname->setValue('Konstantin');
+
+        $page->findButton($submitVia)->click();
+
+        if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) {
+            $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText());
+        } else {
+            $this->fail('Form was never submitted');
+        }
+    }
+
+    public function formSubmitWaysDataProvider()
+    {
+        return array(
+            array('Save'),
+            array('input-type-image'),
+            array('button-without-type'),
+            array('button-type-submit'),
+        );
+    }
+
+    public function testFormSubmit()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/basic_form.html'));
+
+        $webAssert = $this->getAssertSession();
+        $webAssert->fieldExists('first_name')->setValue('Konstantin');
+
+        $webAssert->elementExists('xpath', 'descendant-or-self::form[1]')->submit();
+
+        if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) {
+            $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText());
+        };
+    }
+
+    public function testFormSubmitWithoutButton()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/form_without_button.html'));
+
+        $webAssert = $this->getAssertSession();
+        $webAssert->fieldExists('first_name')->setValue('Konstantin');
+
+        $webAssert->elementExists('xpath', 'descendant-or-self::form[1]')->submit();
+
+        if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) {
+            $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText());
+        };
+    }
+
+    public function testBasicGetForm()
+    {
+        $this->getSession()->visit($this->pathTo('/basic_get_form.php'));
+        $webAssert = $this->getAssertSession();
+
+        $page = $this->getSession()->getPage();
+        $this->assertEquals('Basic Get Form Page', $webAssert->elementExists('css', 'h1')->getText());
+
+        $search = $webAssert->fieldExists('q');
+        $search->setValue('some#query');
+        $page->pressButton('Find');
+
+        $div = $webAssert->elementExists('css', 'div');
+        $this->assertEquals('some#query', $div->getText());
+    }
+
+    public function testAdvancedForm()
+    {
+        $this->getSession()->visit($this->pathTo('/advanced_form.html'));
+        $page = $this->getSession()->getPage();
+
+        $page->fillField('first_name', 'ever');
+        $page->fillField('last_name', 'zet');
+
+        $page->pressButton('Register');
+
+        $this->assertContains('no file', $page->getContent());
+
+        $this->getSession()->visit($this->pathTo('/advanced_form.html'));
+
+        $webAssert = $this->getAssertSession();
+        $page = $this->getSession()->getPage();
+        $this->assertEquals('ADvanced Form Page', $webAssert->elementExists('css', 'h1')->getText());
+
+        $firstname   = $webAssert->fieldExists('first_name');
+        $lastname    = $webAssert->fieldExists('lastn');
+        $email       = $webAssert->fieldExists('Your email:');
+        $select      = $webAssert->fieldExists('select_number');
+        $sex         = $webAssert->fieldExists('sex');
+        $maillist    = $webAssert->fieldExists('mail_list');
+        $agreement   = $webAssert->fieldExists('agreement');
+        $notes       = $webAssert->fieldExists('notes');
+        $about       = $webAssert->fieldExists('about');
+
+        $this->assertEquals('Firstname', $firstname->getValue());
+        $this->assertEquals('Lastname', $lastname->getValue());
+        $this->assertEquals('your@email.com', $email->getValue());
+        $this->assertEquals('20', $select->getValue());
+        $this->assertEquals('w', $sex->getValue());
+        $this->assertEquals('original notes', $notes->getValue());
+
+        $this->assertEquals('on', $maillist->getValue());
+        $this->assertNull($agreement->getValue());
+
+        $this->assertTrue($maillist->isChecked());
+        $this->assertFalse($agreement->isChecked());
+
+        $agreement->check();
+        $this->assertTrue($agreement->isChecked());
+
+        $maillist->uncheck();
+        $this->assertFalse($maillist->isChecked());
+
+        $select->selectOption('thirty');
+        $this->assertEquals('30', $select->getValue());
+
+        $sex->selectOption('m');
+        $this->assertEquals('m', $sex->getValue());
+
+        $notes->setValue('new notes');
+        $this->assertEquals('new notes', $notes->getValue());
+
+        $about->attachFile($this->mapRemoteFilePath(__DIR__ . '/../../web-fixtures/some_file.txt'));
+
+        $button = $page->findButton('Register');
+        $this->assertNotNull($button);
+
+        $page->fillField('first_name', 'Foo "item"');
+        $page->fillField('last_name', 'Bar');
+        $page->fillField('Your email:', 'ever.zet@gmail.com');
+
+        $this->assertEquals('Foo "item"', $firstname->getValue());
+        $this->assertEquals('Bar', $lastname->getValue());
+
+        $button->press();
+
+        if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) {
+            $out = <<<OUT
+array (
+  'agreement' = 'on',
+  'email' = 'ever.zet@gmail.com',
+  'first_name' = 'Foo "item"',
+  'last_name' = 'Bar',
+  'notes' = 'new notes',
+  'select_number' = '30',
+  'sex' = 'm',
+  'submit' = 'Register',
+)
+some_file.txt
+1 uploaded file
+OUT;
+            $this->assertContains($out, $page->getContent());
+        }
+    }
+
+    public function testMultiInput()
+    {
+        $this->getSession()->visit($this->pathTo('/multi_input_form.html'));
+        $page = $this->getSession()->getPage();
+        $webAssert = $this->getAssertSession();
+        $this->assertEquals('Multi input Test', $webAssert->elementExists('css', 'h1')->getText());
+
+        $first = $webAssert->fieldExists('First');
+        $second = $webAssert->fieldExists('Second');
+        $third = $webAssert->fieldExists('Third');
+
+        $this->assertEquals('tag1', $first->getValue());
+        $this->assertSame('tag2', $second->getValue());
+        $this->assertEquals('tag1', $third->getValue());
+
+        $first->setValue('tag2');
+        $this->assertEquals('tag2', $first->getValue());
+        $this->assertSame('tag2', $second->getValue());
+        $this->assertEquals('tag1', $third->getValue());
+
+        $second->setValue('one');
+
+        $this->assertEquals('tag2', $first->getValue());
+        $this->assertSame('one', $second->getValue());
+
+        $third->setValue('tag3');
+
+        $this->assertEquals('tag2', $first->getValue());
+        $this->assertSame('one', $second->getValue());
+        $this->assertEquals('tag3', $third->getValue());
+
+        $button = $page->findButton('Register');
+        $this->assertNotNull($button);
+        $button->press();
+
+        $space = ' ';
+        $out = <<<OUT
+  'tags' =$space
+  array (
+    0 = 'tag2',
+    1 = 'one',
+    2 = 'tag3',
+  ),
+OUT;
+        $this->assertContains($out, $page->getContent());
+    }
+
+    public function testAdvancedFormSecondSubmit()
+    {
+        $this->getSession()->visit($this->pathTo('/advanced_form.html'));
+        $page = $this->getSession()->getPage();
+
+        $button = $page->findButton('Login');
+        $this->assertNotNull($button);
+        $button->press();
+
+        $toSearch = array(
+            "'agreement' = 'off',",
+            "'submit' = 'Login',",
+            'no file',
+        );
+
+        $pageContent = $page->getContent();
+
+        foreach ($toSearch as $searchString) {
+            $this->assertContains($searchString, $pageContent);
+        }
+    }
+
+    public function testSubmitEmptyTextarea()
+    {
+        $this->getSession()->visit($this->pathTo('/empty_textarea.html'));
+        $page = $this->getSession()->getPage();
+
+        $page->pressButton('Save');
+
+        $toSearch = array(
+            "'textarea' = '',",
+            "'submit' = 'Save',",
+            'no file',
+        );
+
+        $pageContent = $page->getContent();
+
+        foreach ($toSearch as $searchString) {
+            $this->assertContains($searchString, $pageContent);
+        }
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/Html5Test.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/Html5Test.php
new file mode 100644
index 0000000..b7f4716
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/Html5Test.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Form;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class Html5Test extends TestCase
+{
+    public function testHtml5FormInputAttribute()
+    {
+        $this->getSession()->visit($this->pathTo('/html5_form.html'));
+        $page = $this->getSession()->getPage();
+        $webAssert = $this->getAssertSession();
+
+        $firstName = $webAssert->fieldExists('first_name');
+        $lastName = $webAssert->fieldExists('last_name');
+
+        $this->assertEquals('not set', $lastName->getValue());
+        $firstName->setValue('John');
+        $lastName->setValue('Doe');
+
+        $this->assertEquals('Doe', $lastName->getValue());
+
+        $page->pressButton('Submit in form');
+
+        if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) {
+            $out = <<<OUT
+  'first_name' = 'John',
+  'last_name' = 'Doe',
+OUT;
+            $this->assertContains($out, $page->getContent());
+            $this->assertNotContains('other_field', $page->getContent());
+        }
+    }
+
+    public function testHtml5FormRadioAttribute()
+    {
+        $this->getSession()->visit($this->pathTo('html5_radio.html'));
+        $page = $this->getSession()->getPage();
+
+        $radio = $this->findById('sex_f');
+        $otherRadio = $this->findById('sex_invalid');
+
+        $this->assertEquals('f', $radio->getValue());
+        $this->assertEquals('invalid', $otherRadio->getValue());
+
+        $radio->selectOption('m');
+
+        $this->assertEquals('m', $radio->getValue());
+        $this->assertEquals('invalid', $otherRadio->getValue());
+
+        $page->pressButton('Submit in form');
+
+        $out = <<<OUT
+  'sex' = 'm',
+OUT;
+        $this->assertContains($out, $page->getContent());
+    }
+
+    public function testHtml5FormButtonAttribute()
+    {
+        $this->getSession()->visit($this->pathTo('/html5_form.html'));
+        $page = $this->getSession()->getPage();
+        $webAssert = $this->getAssertSession();
+
+        $firstName = $webAssert->fieldExists('first_name');
+        $lastName = $webAssert->fieldExists('last_name');
+
+        $firstName->setValue('John');
+        $lastName->setValue('Doe');
+
+        $page->pressButton('Submit outside form');
+
+        if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) {
+            $out = <<<OUT
+  'first_name' = 'John',
+  'last_name' = 'Doe',
+  'submit_button' = 'test',
+OUT;
+            $this->assertContains($out, $page->getContent());
+        }
+    }
+
+    public function testHtml5FormOutside()
+    {
+        $this->getSession()->visit($this->pathTo('/html5_form.html'));
+        $page = $this->getSession()->getPage();
+
+        $page->fillField('other_field', 'hello');
+
+        $page->pressButton('Submit separate form');
+
+        if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) {
+            $out = <<<OUT
+  'other_field' = 'hello',
+OUT;
+            $this->assertContains($out, $page->getContent());
+            $this->assertNotContains('first_name', $page->getContent());
+        }
+    }
+
+    public function testHtml5Types()
+    {
+        $this->getSession()->visit($this->pathTo('html5_types.html'));
+        $page = $this->getSession()->getPage();
+
+        $page->fillField('url', 'http://mink.behat.org/');
+        $page->fillField('email', 'mink@example.org');
+        $page->fillField('number', '6');
+        $page->fillField('search', 'mink');
+        $page->fillField('date', '2014-05-19');
+        $page->fillField('color', '#ff00aa');
+
+        $page->pressButton('Submit');
+
+        $out = <<<OUT
+  'color' = '#ff00aa',
+  'date' = '2014-05-19',
+  'email' = 'mink@example.org',
+  'number' = '6',
+  'search' = 'mink',
+  'submit_button' = 'Submit',
+  'url' = 'http://mink.behat.org/',
+OUT;
+
+        $this->assertContains($out, $page->getContent());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/RadioTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/RadioTest.php
new file mode 100644
index 0000000..39cd743
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/RadioTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Form;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class RadioTest extends TestCase
+{
+    protected function setUp()
+    {
+        $this->getSession()->visit($this->pathTo('radio.html'));
+    }
+
+    public function testIsChecked()
+    {
+        $option = $this->findById('first');
+        $option2 = $this->findById('second');
+
+        $this->assertTrue($option->isChecked());
+        $this->assertFalse($option2->isChecked());
+
+        $option2->selectOption('updated');
+
+        $this->assertFalse($option->isChecked());
+        $this->assertTrue($option2->isChecked());
+    }
+
+    public function testSelectOption()
+    {
+        $option = $this->findById('first');
+
+        $this->assertEquals('set', $option->getValue());
+
+        $option->selectOption('updated');
+
+        $this->assertEquals('updated', $option->getValue());
+
+        $option->selectOption('set');
+
+        $this->assertEquals('set', $option->getValue());
+    }
+
+    public function testSetValue()
+    {
+        $option = $this->findById('first');
+
+        $this->assertEquals('set', $option->getValue());
+
+        $option->setValue('updated');
+
+        $this->assertEquals('updated', $option->getValue());
+        $this->assertFalse($option->isChecked());
+    }
+
+    public function testSameNameInMultipleForms()
+    {
+        $option1 = $this->findById('reused_form1');
+        $option2 = $this->findById('reused_form2');
+
+        $this->assertEquals('test2', $option1->getValue());
+        $this->assertEquals('test3', $option2->getValue());
+
+        $option1->selectOption('test');
+
+        $this->assertEquals('test', $option1->getValue());
+        $this->assertEquals('test3', $option2->getValue());
+    }
+
+    /**
+     * @see https://github.com/Behat/MinkSahiDriver/issues/32
+     */
+    public function testSetValueXPathEscaping()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/advanced_form.html'));
+        $page = $session->getPage();
+
+        $sex = $page->find('xpath', '//*[@name = "sex"]' . "\n|\n" . '//*[@id = "sex"]');
+        $this->assertNotNull($sex, 'xpath with line ending works');
+
+        $sex->setValue('m');
+        $this->assertEquals('m', $sex->getValue(), 'no double xpath escaping during radio button value change');
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/SelectTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/SelectTest.php
new file mode 100644
index 0000000..492625a
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/SelectTest.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Form;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class SelectTest extends TestCase
+{
+    public function testMultiselect()
+    {
+        $this->getSession()->visit($this->pathTo('/multiselect_form.html'));
+        $webAssert = $this->getAssertSession();
+        $page = $this->getSession()->getPage();
+        $this->assertEquals('Multiselect Test', $webAssert->elementExists('css', 'h1')->getText());
+
+        $select      = $webAssert->fieldExists('select_number');
+        $multiSelect = $webAssert->fieldExists('select_multiple_numbers[]');
+        $secondMultiSelect = $webAssert->fieldExists('select_multiple_values[]');
+
+        $this->assertEquals('20', $select->getValue());
+        $this->assertSame(array(), $multiSelect->getValue());
+        $this->assertSame(array('2', '3'), $secondMultiSelect->getValue());
+
+        $select->selectOption('thirty');
+        $this->assertEquals('30', $select->getValue());
+
+        $multiSelect->selectOption('one', true);
+
+        $this->assertSame(array('1'), $multiSelect->getValue());
+
+        $multiSelect->selectOption('three', true);
+
+        $this->assertEquals(array('1', '3'), $multiSelect->getValue());
+
+        $secondMultiSelect->selectOption('two');
+        $this->assertSame(array('2'), $secondMultiSelect->getValue());
+
+        $button = $page->findButton('Register');
+        $this->assertNotNull($button);
+        $button->press();
+
+        $space = ' ';
+        $out = <<<OUT
+  'agreement' = 'off',
+  'select_multiple_numbers' =$space
+  array (
+    0 = '1',
+    1 = '3',
+  ),
+  'select_multiple_values' =$space
+  array (
+    0 = '2',
+  ),
+  'select_number' = '30',
+OUT;
+        $this->assertContains($out, $page->getContent());
+    }
+
+    /**
+     * @dataProvider testElementSelectedStateCheckDataProvider
+     */
+    public function testElementSelectedStateCheck($selectName, $optionValue, $optionText)
+    {
+        $session = $this->getSession();
+        $webAssert = $this->getAssertSession();
+        $session->visit($this->pathTo('/multiselect_form.html'));
+        $select = $webAssert->fieldExists($selectName);
+
+        $optionValueEscaped = $session->getSelectorsHandler()->xpathLiteral($optionValue);
+        $option = $webAssert->elementExists('named', array('option', $optionValueEscaped));
+
+        $this->assertFalse($option->isSelected());
+        $select->selectOption($optionText);
+        $this->assertTrue($option->isSelected());
+    }
+
+    public function testElementSelectedStateCheckDataProvider()
+    {
+        return array(
+            array('select_number', '30', 'thirty'),
+            array('select_multiple_numbers[]', '2', 'two'),
+        );
+    }
+
+    public function testSetValueSingleSelect()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/multiselect_form.html'));
+        $select = $this->getAssertSession()->fieldExists('select_number');
+
+        $select->setValue('10');
+        $this->assertEquals('10', $select->getValue());
+    }
+
+    public function testSetValueMultiSelect()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/multiselect_form.html'));
+        $select = $this->getAssertSession()->fieldExists('select_multiple_values[]');
+
+        $select->setValue(array('1', '2'));
+        $this->assertEquals(array('1', '2'), $select->getValue());
+    }
+
+    /**
+     * @see https://github.com/Behat/Mink/issues/193
+     */
+    public function testOptionWithoutValue()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/issue193.html'));
+
+        $session->getPage()->selectFieldOption('options-without-values', 'Two');
+        $this->assertEquals('Two', $this->findById('options-without-values')->getValue());
+
+        $this->assertTrue($this->findById('two')->isSelected());
+        $this->assertFalse($this->findById('one')->isSelected());
+
+        $session->getPage()->selectFieldOption('options-with-values', 'two');
+        $this->assertEquals('two', $this->findById('options-with-values')->getValue());
+    }
+
+    /**
+     * @see https://github.com/Behat/Mink/issues/131
+     */
+    public function testAccentuatedOption()
+    {
+        $this->getSession()->visit($this->pathTo('/issue131.html'));
+        $page = $this->getSession()->getPage();
+
+        $page->selectFieldOption('foobar', 'Gimme some accentuÃ©s characters');
+
+        $this->assertEquals('1', $page->findField('foobar')->getValue());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Js/ChangeEventTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Js/ChangeEventTest.php
new file mode 100644
index 0000000..d1fd3f1
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Js/ChangeEventTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Js;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class ChangeEventTest extends TestCase
+{
+    /**
+     * 'change' event should be fired after selecting an <option> in a <select>
+     *
+     * TODO check whether this test is redundant with other change event tests.
+     */
+    public function testIssue255()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/issue255.html'));
+
+        $session->getPage()->selectFieldOption('foo_select', 'Option 3');
+
+        $session->wait(2000, '$("#output_foo_select").text() != ""');
+        $this->assertEquals('onChangeSelect', $session->getPage()->find('css', '#output_foo_select')->getText());
+    }
+
+    public function testIssue178()
+    {
+        $session = $this->getSession();
+        $session->visit($this->pathTo('/issue178.html'));
+
+        $this->findById('source')->setValue('foo');
+        $this->assertEquals('foo', $this->findById('target')->getText());
+    }
+
+    /**
+     * @dataProvider setValueChangeEventDataProvider
+     * @group change-event-detector
+     */
+    public function testSetValueChangeEvent($elementId, $valueForEmpty, $valueForFilled = '')
+    {
+        $this->getSession()->visit($this->pathTo('/element_change_detector.html'));
+        $page = $this->getSession()->getPage();
+
+        $input = $this->findById($elementId);
+        $this->assertNull($page->findById($elementId.'-result'));
+
+        // Verify setting value, when control is initially empty.
+        $input->setValue($valueForEmpty);
+        $this->assertElementChangeCount($elementId, 'initial value setting triggers change event');
+
+        if ($valueForFilled) {
+            // Verify setting value, when control already has a value.
+            $this->findById('results')->click();
+            $input->setValue($valueForFilled);
+            $this->assertElementChangeCount($elementId, 'value change triggers change event');
+        }
+    }
+
+    public function setValueChangeEventDataProvider()
+    {
+        return array(
+            'input default' => array('the-input-default', 'from empty', 'from existing'),
+            'input text' => array('the-input-text', 'from empty', 'from existing'),
+            'input email' => array('the-email', 'from empty', 'from existing'),
+            'textarea' => array('the-textarea', 'from empty', 'from existing'),
+            'file' => array('the-file', 'from empty', 'from existing'),
+            'select' => array('the-select', '30'),
+            'radio' => array('the-radio-m', 'm'),
+        );
+    }
+
+    /**
+     * @dataProvider selectOptionChangeEventDataProvider
+     * @group change-event-detector
+     */
+    public function testSelectOptionChangeEvent($elementId, $elementValue)
+    {
+        $this->getSession()->visit($this->pathTo('/element_change_detector.html'));
+        $page = $this->getSession()->getPage();
+
+        $input = $this->findById($elementId);
+        $this->assertNull($page->findById($elementId.'-result'));
+
+        $input->selectOption($elementValue);
+        $this->assertElementChangeCount($elementId);
+    }
+
+    public function selectOptionChangeEventDataProvider()
+    {
+        return array(
+            'select' => array('the-select', '30'),
+            'radio' => array('the-radio-m', 'm'),
+        );
+    }
+
+    /**
+     * @dataProvider checkboxTestWayDataProvider
+     * @group change-event-detector
+     */
+    public function testCheckChangeEvent($useSetValue)
+    {
+        $this->getSession()->visit($this->pathTo('/element_change_detector.html'));
+        $page = $this->getSession()->getPage();
+
+        $checkbox = $this->findById('the-unchecked-checkbox');
+        $this->assertNull($page->findById('the-unchecked-checkbox-result'));
+
+        if ($useSetValue) {
+            $checkbox->setValue(true);
+        } else {
+            $checkbox->check();
+        }
+
+        $this->assertElementChangeCount('the-unchecked-checkbox');
+    }
+
+    /**
+     * @dataProvider checkboxTestWayDataProvider
+     * @group change-event-detector
+     */
+    public function testUncheckChangeEvent($useSetValue)
+    {
+        $this->getSession()->visit($this->pathTo('/element_change_detector.html'));
+        $page = $this->getSession()->getPage();
+
+        $checkbox = $this->findById('the-checked-checkbox');
+        $this->assertNull($page->findById('the-checked-checkbox-result'));
+
+        if ($useSetValue) {
+            $checkbox->setValue(false);
+        } else {
+            $checkbox->uncheck();
+        }
+
+        $this->assertElementChangeCount('the-checked-checkbox');
+    }
+
+    public function checkboxTestWayDataProvider()
+    {
+        return array(
+            array(true),
+            array(false),
+        );
+    }
+
+    private function assertElementChangeCount($elementId, $message = '')
+    {
+        $counterElement = $this->getSession()->getPage()->findById($elementId.'-result');
+        $actualCount = null === $counterElement ? 0 : $counterElement->getText();
+
+        $this->assertEquals('1', $actualCount, $message);
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Js/EventsTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Js/EventsTest.php
new file mode 100644
index 0000000..00d084b
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Js/EventsTest.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Js;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class EventsTest extends TestCase
+{
+    /**
+     * @group mouse-events
+     */
+    public function testClick()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $clicker = $this->getAssertSession()->elementExists('css', '.elements div#clicker');
+        $this->assertEquals('not clicked', $clicker->getText());
+
+        $clicker->click();
+        $this->assertEquals('single clicked', $clicker->getText());
+    }
+
+    /**
+     * @group mouse-events
+     */
+    public function testDoubleClick()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $clicker = $this->getAssertSession()->elementExists('css', '.elements div#clicker');
+        $this->assertEquals('not clicked', $clicker->getText());
+
+        $clicker->doubleClick();
+        $this->assertEquals('double clicked', $clicker->getText());
+    }
+
+    /**
+     * @group mouse-events
+     */
+    public function testRightClick()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $clicker = $this->getAssertSession()->elementExists('css', '.elements div#clicker');
+        $this->assertEquals('not clicked', $clicker->getText());
+
+        $clicker->rightClick();
+        $this->assertEquals('right clicked', $clicker->getText());
+    }
+
+    /**
+     * @group mouse-events
+     */
+    public function testFocus()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $focusBlurDetector = $this->getAssertSession()->elementExists('css', '.elements input#focus-blur-detector');
+        $this->assertEquals('no action detected', $focusBlurDetector->getValue());
+
+        $focusBlurDetector->focus();
+        $this->assertEquals('focused', $focusBlurDetector->getValue());
+    }
+
+    /**
+     * @group mouse-events
+     * @depends testFocus
+     */
+    public function testBlur()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $focusBlurDetector = $this->getAssertSession()->elementExists('css', '.elements input#focus-blur-detector');
+        $this->assertEquals('no action detected', $focusBlurDetector->getValue());
+
+        $focusBlurDetector->blur();
+        $this->assertEquals('blured', $focusBlurDetector->getValue());
+    }
+
+    /**
+     * @group mouse-events
+     */
+    public function testMouseOver()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $mouseOverDetector = $this->getAssertSession()->elementExists('css', '.elements div#mouseover-detector');
+        $this->assertEquals('no mouse action detected', $mouseOverDetector->getText());
+
+        $mouseOverDetector->mouseOver();
+        $this->assertEquals('mouse overed', $mouseOverDetector->getText());
+    }
+
+    /**
+     * @dataProvider provideKeyboardEventsModifiers
+     */
+    public function testKeyboardEvents($modifier, $eventProperties)
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $webAssert = $this->getAssertSession();
+
+        $input1 = $webAssert->elementExists('css', '.elements input.input.first');
+        $input2 = $webAssert->elementExists('css', '.elements input.input.second');
+        $input3 = $webAssert->elementExists('css', '.elements input.input.third');
+        $event  = $webAssert->elementExists('css', '.elements .text-event');
+
+        $input1->keyDown('u', $modifier);
+        $this->assertEquals('key downed:' . $eventProperties, $event->getText());
+
+        $input2->keyPress('r', $modifier);
+        $this->assertEquals('key pressed:114 / ' . $eventProperties, $event->getText());
+
+        $input3->keyUp(78, $modifier);
+        $this->assertEquals('key upped:78 / ' . $eventProperties, $event->getText());
+    }
+
+    public function provideKeyboardEventsModifiers()
+    {
+        return array(
+            'none' => array(null, '0 / 0 / 0 / 0'),
+            'alt' => array('alt', '1 / 0 / 0 / 0'),
+             // jQuery considers ctrl as being a metaKey in the normalized event
+            'ctrl' => array('ctrl', '0 / 1 / 0 / 1'),
+            'shift' => array('shift', '0 / 0 / 1 / 0'),
+            'meta' => array('meta', '0 / 0 / 0 / 1'),
+        );
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Js/JavascriptEvaluationTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Js/JavascriptEvaluationTest.php
new file mode 100644
index 0000000..00235d1
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Js/JavascriptEvaluationTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Js;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class JavascriptEvaluationTest extends TestCase
+{
+    /**
+     * Tests, that `wait` method returns check result after exit.
+     */
+    public function testWaitReturnValue()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+
+        $found = $this->getSession()->wait(5000, '$("#draggable").length == 1');
+        $this->assertTrue($found);
+    }
+
+    public function testWait()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+
+        $waitable = $this->findById('waitable');
+
+        $waitable->click();
+        $this->getSession()->wait(3000, '$("#waitable").has("div").length > 0');
+        $this->assertEquals('arrived', $this->getSession()->getPage()->find('css', '#waitable > div')->getText());
+
+        $waitable->click();
+        $this->getSession()->wait(3000, 'false');
+        $this->assertEquals('timeout', $this->getSession()->getPage()->find('css', '#waitable > div')->getText());
+    }
+
+    /**
+     * @dataProvider provideExecutedScript
+     */
+    public function testExecuteScript($script)
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->getSession()->executeScript($script);
+
+        sleep(1);
+
+        $heading = $this->getAssertSession()->elementExists('css', 'h1');
+        $this->assertEquals('Hello world', $heading->getText());
+    }
+
+    public function provideExecutedScript()
+    {
+        return array(
+            array('document.querySelector("h1").textContent = "Hello world"'),
+            array('document.querySelector("h1").textContent = "Hello world";'),
+            array('function () {document.querySelector("h1").textContent = "Hello world";}()'),
+            array('function () {document.querySelector("h1").textContent = "Hello world";}();'),
+            array('(function () {document.querySelector("h1").textContent = "Hello world";})()'),
+            array('(function () {document.querySelector("h1").textContent = "Hello world";})();'),
+        );
+    }
+
+    /**
+     * @dataProvider provideEvaluatedScript
+     */
+    public function testEvaluateJavascript($script)
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+
+        $this->assertSame(2, $this->getSession()->evaluateScript($script));
+    }
+
+    public function provideEvaluatedScript()
+    {
+        return array(
+            array('1 + 1'),
+            array('1 + 1;'),
+            array('return 1 + 1'),
+            array('return 1 + 1;'),
+            array('function () {return 1+1;}()'),
+            array('(function () {return 1+1;})()'),
+            array('return function () { return 1+1;}()'),
+            array('return (function () {return 1+1;})()'),
+        );
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Js/JavascriptTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Js/JavascriptTest.php
new file mode 100644
index 0000000..86cfa65
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Js/JavascriptTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Js;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class JavascriptTest extends TestCase
+{
+    public function testAriaRoles()
+    {
+        $this->getSession()->visit($this->pathTo('/aria_roles.html'));
+
+        $this->getSession()->wait(5000, '$("#hidden-element").is(":visible") === false');
+        $this->getSession()->getPage()->pressButton('Toggle');
+        $this->getSession()->wait(5000, '$("#hidden-element").is(":visible") === true');
+
+        $this->getSession()->getPage()->clickLink('Go to Index');
+        $this->assertEquals($this->pathTo('/index.html'), $this->getSession()->getCurrentUrl());
+    }
+
+    public function testDragDrop()
+    {
+        $this->getSession()->visit($this->pathTo('/js_test.html'));
+        $webAssert = $this->getAssertSession();
+
+        $draggable = $webAssert->elementExists('css', '#draggable');
+        $droppable = $webAssert->elementExists('css', '#droppable');
+
+        $draggable->dragTo($droppable);
+        $this->assertEquals('Dropped!', $droppable->find('css', 'p')->getText());
+    }
+
+    // test accentuated char in button
+    public function testIssue225()
+    {
+        $this->getSession()->visit($this->pathTo('/issue225.html'));
+        $this->getSession()->getPage()->pressButton('CrÃ©er un compte');
+        $this->getSession()->wait(5000, '$("#panel").text() != ""');
+
+        $this->assertContains('OH AIH!', $this->getSession()->getPage()->getText());
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Js/WindowTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Js/WindowTest.php
new file mode 100644
index 0000000..70b9ed9
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/Js/WindowTest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver\Js;
+
+use Behat\Mink\Tests\Driver\TestCase;
+
+class WindowTest extends TestCase
+{
+    public function testWindow()
+    {
+        $this->getSession()->visit($this->pathTo('/window.html'));
+        $session = $this->getSession();
+        $page    = $session->getPage();
+        $webAssert = $this->getAssertSession();
+
+        $page->clickLink('Popup #1');
+        $session->switchToWindow(null);
+
+        $page->clickLink('Popup #2');
+        $session->switchToWindow(null);
+
+        $el = $webAssert->elementExists('css', '#text');
+        $this->assertSame('Main window div text', $el->getText());
+
+        $session->switchToWindow('popup_1');
+        $el = $webAssert->elementExists('css', '#text');
+        $this->assertSame('Popup#1 div text', $el->getText());
+
+        $session->switchToWindow('popup_2');
+        $el = $webAssert->elementExists('css', '#text');
+        $this->assertSame('Popup#2 div text', $el->getText());
+
+        $session->switchToWindow(null);
+        $el = $webAssert->elementExists('css', '#text');
+        $this->assertSame('Main window div text', $el->getText());
+    }
+
+    public function testGetWindowNames()
+    {
+        $this->getSession()->visit($this->pathTo('/window.html'));
+        $session = $this->getSession();
+        $page    = $session->getPage();
+
+        $windowName = $this->getSession()->getWindowName();
+
+        $this->assertNotNull($windowName);
+
+        $page->clickLink('Popup #1');
+        $page->clickLink('Popup #2');
+
+        $windowNames = $this->getSession()->getWindowNames();
+
+        $this->assertNotNull($windowNames[0]);
+        $this->assertNotNull($windowNames[1]);
+        $this->assertNotNull($windowNames[2]);
+    }
+
+    public function testResizeWindow()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+        $session = $this->getSession();
+
+        $session->resizeWindow(400, 300);
+        $session->wait(1000, 'false');
+
+        $script = "return Math.abs(window.outerHeight - 300) <= 100 && Math.abs(window.outerWidth - 400) <= 100;";
+
+        $this->assertTrue($session->evaluateScript($script));
+    }
+
+    public function testWindowMaximize()
+    {
+        $this->getSession()->visit($this->pathTo('/index.html'));
+        $session = $this->getSession();
+
+        $session->maximizeWindow();
+        $session->wait(1000, 'false');
+
+        $script = "return Math.abs(screen.availHeight - window.outerHeight) <= 100;";
+
+        $this->assertTrue($session->evaluateScript($script));
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/tests/TestCase.php b/core/vendor/behat/mink/driver-testsuite/tests/TestCase.php
new file mode 100644
index 0000000..22860f6
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/tests/TestCase.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver;
+
+use Behat\Mink\Exception\UnsupportedDriverActionException;
+use Behat\Mink\Mink;
+use Behat\Mink\Session;
+use Behat\Mink\WebAssert;
+
+abstract class TestCase extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Mink session manager.
+     *
+     * @var Mink
+     */
+    private static $mink;
+
+    /**
+     * @var AbstractConfig
+     */
+    private static $config;
+
+    /**
+     * Initializes the test case
+     */
+    public static function setUpBeforeClass()
+    {
+        if (null === self::$mink) {
+            $session = new Session(self::getConfig()->createDriver());
+            self::$mink = new Mink(array('sess' => $session));
+        }
+    }
+
+    /**
+     * @return AbstractConfig
+     *
+     * @throws \UnexpectedValueException if the global driver_config_factory returns an invalid object
+     */
+    private static function getConfig()
+    {
+        if (null === self::$config) {
+            self::$config = call_user_func($GLOBALS['driver_config_factory']);
+
+            if (!self::$config instanceof AbstractConfig) {
+                throw new \UnexpectedValueException('The "driver_config_factory" global variable must return a \Behat\Mink\Tests\Driver\AbstractConfig.');
+            }
+        }
+
+        return self::$config;
+    }
+
+    protected function checkRequirements()
+    {
+        if (null !== $message = self::getConfig()->skipMessage(get_class($this), $this->getName(false))) {
+            $this->markTestSkipped($message);
+        }
+
+        parent::checkRequirements();
+    }
+
+    protected function tearDown()
+    {
+        if (null !== self::$mink) {
+            self::$mink->resetSessions();
+        }
+    }
+
+    protected function onNotSuccessfulTest(\Exception $e)
+    {
+        if ($e instanceof UnsupportedDriverActionException) {
+            $this->markTestSkipped($e->getMessage());
+        }
+
+        parent::onNotSuccessfulTest($e);
+    }
+
+    /**
+     * Returns session.
+     *
+     * @return Session
+     */
+    protected function getSession()
+    {
+        return self::$mink->getSession('sess');
+    }
+
+    /**
+     * Returns assert session.
+     *
+     * @return WebAssert
+     */
+    protected function getAssertSession()
+    {
+        return self::$mink->assertSession('sess');
+    }
+
+    /**
+     * @param string $id
+     *
+     * @return \Behat\Mink\Element\NodeElement
+     */
+    protected function findById($id)
+    {
+        $id = $this->getSession()->getSelectorsHandler()->xpathLiteral($id);
+
+        return $this->getAssertSession()->elementExists('named', array('id', $id));
+    }
+
+    /**
+     * Map remote file path.
+     *
+     * @param string $file File path.
+     *
+     * @return string
+     */
+    protected function mapRemoteFilePath($file)
+    {
+        $realPath = realpath($file);
+
+        if (false !== $realPath) {
+            $file = $realPath;
+        }
+
+        return self::getConfig()->mapRemoteFilePath($file);
+    }
+
+    /**
+     * @param string $path
+     *
+     * @return string
+     */
+    protected function pathTo($path)
+    {
+        return rtrim(self::getConfig()->getWebFixturesUrl(), '/') . '/' . ltrim($path, '/');
+    }
+
+    /**
+     * Waits for a condition to be true, considering than it is successful for drivers not supporting wait()
+     *
+     * @param int    $time
+     * @param string $condition A JS condition to evaluate
+     *
+     * @return bool
+     *
+     * @see \Behat\Mink\Session::wait()
+     */
+    protected function safePageWait($time, $condition)
+    {
+        try {
+            return $this->getSession()->wait($time, $condition);
+        } catch (UnsupportedDriverActionException $e) {
+            return true;
+        }
+    }
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/404.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/404.php
new file mode 100644
index 0000000..d16db99
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/404.php
@@ -0,0 +1,2 @@
+<?php header("HTTP/1.0 404 Not Found") ?>
+Sorry, page not found
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/500.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/500.php
new file mode 100644
index 0000000..c43efcb
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/500.php
@@ -0,0 +1,2 @@
+<?php header("HTTP/1.0 500 Server Error") ?>
+Sorry, a server error happened
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form.html
new file mode 100644
index 0000000..a2392c0
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>ADvanced Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>ADvanced Form Page</h1>
+
+    <form method="POST" enctype="multipart/form-data" action="advanced_form_post.php">
+        <input name="first_name" value="Firstname" type="text" />
+        <input id="lastn" name="last_name" value="Lastname" type="text" />
+        <label for="email">
+            Your email:
+            <input type="email" id="email" name="email" value="your@email.com" />
+        </label>
+
+        <select name="select_number">
+            <option value="10">ten</option>
+            <option selected="selected" value="20">twenty</option>
+            <option value="30">thirty</option>
+        </select>
+
+        <label>
+            <span><input type="radio" name="sex" value="m" /> m</span>
+            <span><input type="radio" name="sex" value="w" checked="checked" /> w</span>
+        </label>
+
+        <input type="checkbox" name="mail_list" checked="checked" value="on"/>
+        <input type="checkbox" name="agreement" value="yes"/>
+
+        <textarea name="notes">original notes</textarea>
+
+        <input type="file" name="about" />
+
+        <input type="submit" name="submit" value="Register" />
+        <input type="submit" name="submit" value="Login" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form_post.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form_post.php
new file mode 100644
index 0000000..755806d
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form_post.php
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Advanced form save</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+</head>
+<body>
+<?php
+error_reporting(0);
+
+if (isset($_POST['select_multiple_numbers']) && false !== strpos($_POST['select_multiple_numbers'][0], ',')) {
+    $_POST['select_multiple_numbers'] = explode(',', $_POST['select_multiple_numbers'][0]);
+}
+
+$_POST['agreement'] = isset($_POST['agreement']) ? 'on' : 'off';
+ksort($_POST);
+echo str_replace('>', '', var_export($_POST, true)) . "\n";
+if (isset($_FILES['about']) && file_exists($_FILES['about']['tmp_name'])) {
+    echo $_FILES['about']['name'] . "\n";
+    echo file_get_contents($_FILES['about']['tmp_name']);
+} else {
+    echo "no file";
+}
+?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/aria_roles.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/aria_roles.html
new file mode 100644
index 0000000..7074e85
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/aria_roles.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>ARIA roles test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+	This page tests selected ARIA roles<br />
+	(see <a href="http://www.w3.org/TR/wai-aria/">http://www.w3.org/TR/wai-aria/</a>)
+
+	<div id="hidden-element-toggle-button" role="button">Toggle</div>
+	<div id="hidden-element" style="display:none;">This content's visibility is changed by clicking the Toggle Button.</div>
+
+	<!-- This link is created programmatically -->
+	<div id="link-element"></div>
+
+    <script language="javascript" type="text/javascript" src="js/jquery-1.6.2-min.js"></script>
+	<script language="javascript" type="text/javascript">
+		$(document).ready(function() {
+			$('#hidden-element-toggle-button').click(function() {
+				$('#hidden-element').toggle();
+			});
+
+			$('#link-element').attr('role', 'link').text('Go to Index').click(function() {
+        window.location.href = 'index.html';
+			});
+		});
+	</script>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_auth.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_auth.php
new file mode 100644
index 0000000..9620a0e
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_auth.php
@@ -0,0 +1,12 @@
+<?php
+$username = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : false;
+$password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : false;
+
+if ($username == 'mink-user' && $password == 'mink-password') {
+    echo 'is authenticated';
+} else {
+    header('WWW-Authenticate: Basic realm="Mink Testing Area"');
+    header('HTTP/1.0 401 Unauthorized');
+
+    echo 'is not authenticated';
+}
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form.html
new file mode 100644
index 0000000..2d330cf
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Basic Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Basic Form Page</h1>
+
+    <form method="POST" action="basic_form_post.php">
+        <input name="first_name" value="Firstname" type="text" />
+        <input id="lastn" name="last_name" value="Lastname" type="text" />
+
+        <input type="reset" id="Reset" />
+
+        <input type="submit" id="Save" />
+        <input type="image" id="input-type-image"/>
+        <button>button-without-type</button>
+        <button type="submit">button-type-submit</button>
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form_post.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form_post.php
new file mode 100644
index 0000000..8a5e340
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form_post.php
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Basic Form Saving</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Anket for <?php echo $_POST['first_name'] ?></h1>
+
+    <span id="first">Firstname: <?php echo $_POST['first_name'] ?></span>
+    <span id="last">Lastname: <?php echo $_POST['last_name'] ?></span>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_get_form.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_get_form.php
new file mode 100644
index 0000000..a0b3516
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_get_form.php
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Basic Get Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Basic Get Form Page</h1>
+
+    <div id="serach">
+        <?php echo isset($_GET['q']) && $_GET['q'] ? $_GET['q'] : 'No search query' ?>
+    </div>
+
+    <form>
+        <input name="q" value="" type="text" />
+
+        <input type="submit" value="Find" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page1.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page1.php
new file mode 100644
index 0000000..7f128ca
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page1.php
@@ -0,0 +1,21 @@
+<?php
+if (!isset($cookieAtRootPath)) {
+    $cookieAtRootPath = true;
+}
+
+if (!isset($cookieValue)) {
+    $cookieValue = 'srv_var_is_set';
+}
+
+setcookie('srvr_cookie', $cookieValue, null, $cookieAtRootPath ? '/' : null);
+?>
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Basic Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    Basic Page With Cookie Set from Server Side
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page2.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page2.php
new file mode 100644
index 0000000..22bcd1b
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page2.php
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Basic Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    Previous cookie: <?php echo isset($_COOKIE['srvr_cookie']) ? $_COOKIE['srvr_cookie'] : 'NO'; ?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page3.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page3.php
new file mode 100644
index 0000000..caa28bc
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page3.php
@@ -0,0 +1,16 @@
+<?php
+
+$hasCookie = isset($_COOKIE['foo']);
+setcookie('foo', 'bar', 0, '/', null, false, true);
+
+?>
+<!DOCTYPE html>
+<html>
+<head>
+    <title>HttpOnly Cookie Test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+	<div id="cookie-status">Has Cookie: <?php echo json_encode($hasCookie) ?></div>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/css_mouse_events.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/css_mouse_events.html
new file mode 100644
index 0000000..750ca54
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/css_mouse_events.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <title>CSS Mouse Events Testing</title>
+        <style type="text/css">
+            .squares div {
+                width: 100px;
+                height: 100px;
+                border: 1px solid black;
+                margin-right: 4px;
+                float: left;
+            }
+
+            .squares div:hover {
+                height: 200px;
+                background-color: red;
+            }
+        </style>
+
+        <script language="javascript" type="text/javascript" src="js/jquery-1.6.2-min.js"></script>
+    </head>
+
+    <body>
+        <div class="squares">
+            <div id="reset-square">reset</div>
+            <div id="action-square">mouse action</div>
+        </div>
+        <script>
+            $('#action-square').bind('contextmenu', function (e) {
+                // Prevent opening the context menu on right click as the browser might consider the
+                // mouse is in the context menu rather than hovering the element
+                e.preventDefault();
+            });
+        </script>
+    </body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/element_change_detector.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/element_change_detector.html
new file mode 100644
index 0000000..af3032a
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/element_change_detector.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>ADvanced Form</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+    <script src="js/jquery-1.6.2-min.js"></script>
+</head>
+<body>
+    <h1>ADvanced Form Page</h1>
+
+    <form method="POST" enctype="multipart/form-data" action="advanced_form_post.php">
+        <input id="the-input-default" value="" />
+        <input type="text" id="the-input-text" value="" />
+        <input type="email" id="the-email" value="" />
+
+        <select id="the-select">
+            <option value="10">ten</option>
+            <option value="20" selected="selected">twenty</option>
+            <option value="30">thirty</option>
+        </select>
+
+        <label>
+            <span><input type="radio" name="sex" id="the-radio-m" value="m" /> m</span>
+            <span><input type="radio" name="sex" id="the-radio-w" value="w" checked="checked" /> w</span>
+        </label>
+
+        <input type="checkbox" id="the-checked-checkbox" value="cb-val" checked/>
+        <input type="checkbox" id="the-unchecked-checkbox" value="cb-val"/>
+
+        <textarea id="the-textarea"></textarea>
+
+        <input type="file" id="the-file" />
+    </form>
+
+    <ul id="results" style="border: 1px solid red;">
+        <li>for easy element location</li>
+    </ul>
+
+    <script type="text/javascript">
+        $(document).ready(function () {
+            var $change_registry = {},
+                $results = $('#results');
+
+            $(':input').change(function ($e) {
+                var $result_id = this.id + '-result';
+
+                if (!$change_registry[this.id]) {
+                    $change_registry[this.id] = 1;
+                    $results.append('<li id="' + $result_id + '"></li>');
+                }
+                else {
+                    $change_registry[this.id]++;
+                }
+
+                $('#' + $result_id).text($change_registry[this.id]);
+            });
+
+            $results.click(function () {
+                $results.empty();
+                $change_registry = {};
+            });
+        });
+    </script>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/empty_textarea.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/empty_textarea.html
new file mode 100644
index 0000000..f0779ee
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/empty_textarea.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Empty textarea submission</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+    <h1>Empty textarea submission</h1>
+    <form method="POST" action="advanced_form_post.php">
+        <textarea name="textarea"></textarea>
+        <input type="submit" name="submit" value="Save" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/form_without_button.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/form_without_button.html
new file mode 100644
index 0000000..8063026
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/form_without_button.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>Form submission without button test</title>
+</head>
+<body>
+    <form action="basic_form_post.php" method="POST">
+        <input name="first_name" type="text" value="not set">
+        <input name="last_name" type="text" value="not set">
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/headers.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/headers.php
new file mode 100644
index 0000000..25d9b90
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/headers.php
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Headers page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <?php print_r($_SERVER); ?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_form.html
new file mode 100644
index 0000000..9070353
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_form.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>HTML5 form attribute test</title>
+</head>
+<body>
+    <form action="advanced_form_post.php" method="POST" id="test-form">
+        <input name="first_name" type="text" value="not set">
+        <input name="other_field" type="text" value="not submitted" form="another">
+        <input type="submit" value="Submit separate form" form="another">
+        <input type="submit" value="Submit in form">
+    </form>
+    <input name="last_name" type="text" form="test-form" value="not set">
+    <button type="submit" form="test-form" name="submit_button" value="test">Submit outside form</button>
+    <form id="another" method="post" action="advanced_form_post.php"></form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_radio.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_radio.html
new file mode 100644
index 0000000..fd54c2d
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_radio.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>HTML5 form attribute test</title>
+</head>
+<body>
+    <form action="advanced_form_post.php" method="POST" id="test-form">
+        <input name="sex" type="radio" value="m" id="sex_m">
+        <input name="sex" type="radio" value="invalid" form="another" id="sex_invalid" checked="checked">
+        <input type="submit" value="Submit in form">
+    </form>
+    <input name="sex" type="radio" form="test-form" value="f" id="sex_f" checked="checked">
+    <form id="another" method="post" action="advanced_form_post.php"></form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_types.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_types.html
new file mode 100644
index 0000000..bd46cfa
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_types.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>HTML5 form attribute test</title>
+</head>
+<body>
+    <form action="advanced_form_post.php" method="POST">
+        <input name="url" type="url">
+        <input name="email" type="email">
+        <input name="number" type="number">
+        <input name="search" type="search">
+        <input name="color" type="color">
+        <input name="date" type="date">
+        <input type="submit" name="submit_button" value="Submit">
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html_decoding.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html_decoding.html
new file mode 100644
index 0000000..341f226
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html_decoding.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>HTML Decoding Test</title>
+</head>
+<body>
+    <div>
+        <span custom-attr="&amp;">some text</span>
+
+        <form method="post" action="index.html">
+            <input value="&amp;"/>
+
+            <input type="submit" value="Send"/>
+        </form>
+    </div>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe.html
new file mode 100644
index 0000000..c54797f
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<iframe src="iframe_inner.html" name="subframe"></iframe>
+
+<div id="text">
+    Main window div text
+</div>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe_inner.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe_inner.html
new file mode 100644
index 0000000..512f058
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe_inner.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+    <div id="text">
+        iFrame div text
+    </div>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/index.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/index.html
new file mode 100644
index 0000000..6bb379c
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/index.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Index page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Extremely useless page</h1>
+    <div id="core">
+        <strong>Lorem</strong> ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim <strong>veniam</strong>, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla <strong class="super-duper">pariatur</strong>. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+        <div id="some-element" data-href="http://mink.behat.org">
+            some <div>very
+            </div>
+<em>interesting</em>      text
+        </div>
+
+        <div class="attribute-testing">
+            <input type="text" id="attr-elem[with-value]" with-value="some-value"/>
+            <input type="text" id="attr-elem[without-value]" without-value/>
+            <input type="text" id="attr-elem[with-empty-value]" with-empty-value=""/>
+            <input type="text" id="attr-elem[with-missing]"/>
+        </div>
+
+        <div class="travers">
+            <div class="sub">el1</div>
+            <div class="sub">el2</div>
+            <div class="sub">
+                <a href="some_url">some <strong>deep</strong> url</a>
+            </div>
+        </div>
+
+        <div class="sub">el4</div>
+
+    </div>
+
+    <footer>
+        <form id="search-form">
+            <input type="text" placeholder="Search site..." />
+        </form>
+        <form id="profile">
+            <div data-custom="something">
+                <label>
+                    <input type="text" id="user-name" name="username" />
+                </label>
+            </div>
+            <input type="submit" />
+        </form>
+    </footer>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue130.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue130.php
new file mode 100644
index 0000000..201d982
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue130.php
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <?php
+    if ('1' === $_GET['p']) {
+        echo '<a href="issue130.php?p=2">Go to 2</a>';
+    } else {
+        echo '<strong>'.$_SERVER['HTTP_REFERER'].'</strong>';
+    }
+    ?>
+</body>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue131.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue131.html
new file mode 100644
index 0000000..fa3427a
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue131.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Issue 131</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+    <h1>There is a non breaking&#160;space</h1>
+    <pre id="foobar">Some accentuÃ©s characters</pre>
+    <form>
+        <select name="foobar">
+          <option value="1">Gimme some accentuÃ©s characters</option>
+        </select>
+        <input type="submit" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue140.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue140.php
new file mode 100644
index 0000000..04a4caf
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue140.php
@@ -0,0 +1,16 @@
+<?php
+if (!empty($_POST)) {
+    setcookie("tc", $_POST['cookie_value'], null, '/');
+} elseif (isset($_GET["show_value"])) {
+    echo $_COOKIE["tc"];
+    die();
+}
+?>
+<!DOCTYPE html>
+<html>
+<body>
+    <form method="post">
+        <input name="cookie_value">
+        <input type="submit" value="Set cookie">
+    </form>
+</body>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue178.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue178.html
new file mode 100644
index 0000000..3efc743
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue178.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>Index page</title>
+</head>
+<body>
+
+<form action="#">
+	<div>
+		<input type="text" name="source" id="source"
+			onkeyup="document.getElementById('target').innerHTML = this.value" value="bar">
+	</div>
+</form>
+
+<div id="target"></div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue193.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue193.html
new file mode 100644
index 0000000..e722a43
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue193.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Index page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+
+<form action="#">
+    <select id="options-without-values" name="without">
+        <option>none selected</option>
+        <option id="one">One</option>
+        <option id="two">Two</option>
+        <option>Three</option>
+    </select>
+
+    <select id="options-with-values" name="with">
+        <option value="none">none selected</option>
+        <option value="one">One</option>
+        <option value="two">Two</option>
+        <option value="three">Three</option>
+    </select>
+</form>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue211.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue211.html
new file mode 100644
index 0000000..bb977ec
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue211.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Index page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+
+<form action="#">
+    <p>
+        <label for="phone">TÃ©lÃ©phone</label>
+        <input type="text" name="phone" id="phone" />
+    </p>
+    <p>
+        <label for="daphone">DaPhone</label>
+        <input type="text" name="daphone" id="daphone" />
+    </p>
+
+    <input type="submit" value="Send" />
+</form>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue212.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue212.html
new file mode 100644
index 0000000..24ae62f
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue212.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <form action="/" method="post" id="user-login-form">
+    <input type="submit" name="toto" id="jaguar-button" value="jaguar"/>
+    <input type="submit" name="toto" id="poney-button" value="poney"/>
+  </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue215.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue215.html
new file mode 100644
index 0000000..adff3fb
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue215.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>Index page</title>
+</head>
+<body>
+
+<form action="#">
+<textarea rows="10" cols="10" id="textarea">
+foo
+bar
+</textarea>
+</form>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue225.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue225.html
new file mode 100644
index 0000000..2178bba
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue225.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+    <title>Index page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+    <script src="js/jquery-1.6.2-min.js"></script>
+</head>
+<body>
+
+<button id="btn">CrÃ©er un compte</button>
+
+<div id="panel"></div>
+
+<script type="text/javascript">
+    $('#btn').click(function (event) {
+        $('#panel').text('OH ' + 'AIH!');
+    });
+</script>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue255.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue255.html
new file mode 100644
index 0000000..d56a427
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue255.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Issue 255</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+    <script src="js/jquery-1.6.2-min.js"></script>
+</head>
+<body>
+    <form>
+        <label for="foo_select">Foo</label>
+        <select name="foo_select" id="foo_select">
+            <option value="1" selected="selected">Option 1</option>
+            <option value="2">Option 2</option>
+            <option value="3">Option 3</option>
+        </select>
+        <input type="checkbox" id="foo_check" />
+        <input type="submit" />
+        <p id="output_foo_select"></p>
+        <p id="output_foo_check"></p>
+    </form>
+
+    <script type="text/javascript">
+        (function() {
+            $('#foo_select').change(function () {
+                $('#output_foo_select').text("onChangeSelect");
+            });
+            $('#foo_check').change(function () {
+                $('#output_foo_check').text("onChangeCheck");
+            });
+        })();
+    </script>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-1.6.2-min.js b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-1.6.2-min.js
new file mode 100644
index 0000000..8cdc80e
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-1.6.2-min.js
@@ -0,0 +1,18 @@
+/*!
+ * jQuery JavaScript Library v1.6.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Jun 30 14:16:56 2011 -0400
+ */
+(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bC.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bR,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bX(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bX(a,c,d,e,"*",g));return l}function bW(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bN),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bA(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bv:bw;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bg(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function W(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(R.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(x,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(H)return H.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g](h)}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u=/\:|^on/,v,w;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(o);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(o);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(n," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.
+shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,N(a.origType,a.selector),f.extend({},a,{handler:M,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,N(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?E:D):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=E;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=E;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=E,this.stopPropagation()},isDefaultPrevented:D,isPropagationStopped:D,isImmediatePropagationStopped:D};var F=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},G=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?G:F,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?G:F)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&f(b).closest("form").length&&K("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&K("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var H,I=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var L={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||D,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=x.exec(h),k="",j&&(k=j[0],h=h.replace(x,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,L[h]?(a.push(L[h]+k),h=h+k):h=(L[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+N(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+N(h,m),e)}return this}}),f.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".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var O=/Until$/,P=/^(?:parents|prevUntil|prevAll)/,Q=/,/,R=/^.[^:#\[\.,]*$/,S=Array.prototype.slice,T=f.expr.match.POS,U={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(W(this,a,!1),"not",a)},filter:function(a){return this.pushStack(W(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=T.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/<tbody/i,ba=/<|&#?\w+;/,bb=/<(?:script|object|embed|option|style)/i,bc=/checked\s*(?:[^=]|=\s*.checked.)/i,bd=/\/(java|ecma)script/i,be=/^\s*<!(?:\[CDATA\[|\-\-)/,bf={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bc.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bg(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bm)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i;b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!bb.test(a[0])&&(f.support.checkClone||!bc.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j
+)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1></$2>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bl(k[i]);else bl(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bd.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bn=/alpha\([^)]*\)/i,bo=/opacity=([^)]*)/,bp=/([A-Z]|^ms)/g,bq=/^-?\d+(?:px)?$/i,br=/^-?\d/,bs=/^[+\-]=/,bt=/[^+\-\.\de]+/g,bu={position:"absolute",visibility:"hidden",display:"block"},bv=["Left","Right"],bw=["Top","Bottom"],bx,by,bz;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bx(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d;if(h==="number"&&isNaN(d)||d==null)return;h==="string"&&bs.test(d)&&(d=+d.replace(bt,"")+parseFloat(f.css(a,c)),h="number"),h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bx)return bx(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bA(a,b,d);f.swap(a,bu,function(){e=bA(a,b,d)});return e}},set:function(a,b){if(!bq.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cs(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cr("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cr("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cs(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cj.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=ck.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cr("show",1),slideUp:cr("hide",1),slideToggle:cr("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function h(a){return d.step(a)}var d=this,e=f.fx,g;this.startTime=cn||cp(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,h.elem=this.elem,h()&&f.timers.push(h)&&!cl&&(co?(cl=!0,g=function(){cl&&(co(g),e.tick())},co(g)):cl=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cn||cp(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cl),cl=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var ct=/^t(?:able|d|h)$/i,cu=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cv(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!ct.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window);
\ No newline at end of file
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-ui-1.8.14.custom.min.js b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-ui-1.8.14.custom.min.js
new file mode 100644
index 0000000..1764e11
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-ui-1.8.14.custom.min.js
@@ -0,0 +1,127 @@
+/*!
+ * jQuery UI 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.14",
+keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();
+b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,
+"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",
+function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,
+outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b);
+return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=
+0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
+a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
+e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
+this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
+widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
+enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*!
+ * jQuery UI Mouse 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ *	jquery.ui.widget.js
+ */
+(function(b){var d=false;b(document).mousedown(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
+this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==
+false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
+!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
+false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Draggable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
+"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
+this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;this.helper=
+this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
+this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
+_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
+false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
+10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
+!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
+a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
+this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
+10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
+10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
+(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
+"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
+10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
+!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
+if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
+b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
+526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
+c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.14"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
+h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
+false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
+this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
+c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
+this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
+a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
+"x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
+c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
+c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
+width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
+o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
+p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
+(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
+10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
+;/*
+ * jQuery UI Droppable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.draggable.js
+ */
+(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
+a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
+this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
+this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
+d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
+a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.14"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
+switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
+i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
+"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
+a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parentsUntil("body").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);if(c=
+!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parentsUntil("body").unbind("scroll.droppable");
+a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
+;
\ No newline at end of file
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/js_test.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js_test.html
new file mode 100644
index 0000000..e965a40
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js_test.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>JS elements test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+
+    <style>
+        #draggable {
+            width: 100px; height: 100px; padding: 0.5em; float: left; margin: 10px 10px 10px 0;
+            background:#ccc;
+            opacity:0.5;
+        }
+        #droppable {
+            width: 150px; height: 150px; padding: 0.5em; float: left; margin: 10px;
+            background:#eee;
+        }
+        #waitable {
+            width: 150px; height: 150px; padding: 0.5em; float: left; margin: 10px;
+            background:#eee;
+        }
+    </style>
+</head>
+<body>
+    <div class="elements">
+        <div id="clicker">not clicked</div>
+        <div id="mouseover-detector">no mouse action detected</div>
+        <div id="invisible" style="display: none">invisible man</div>
+        <input id="focus-blur-detector" type="text" value="no action detected"/>
+        <input class="input first" type="text" value="" />
+        <input class="input second" type="text" value="" />
+        <input class="input third" type="text" value="" />
+        <div class="text-event"></div>
+    </div>
+
+    <div id="draggable" class="ui-widget-content"></div>
+
+    <div id="droppable" class="ui-widget-header">
+        <p>Drop here</p>
+    </div>
+
+    <div id="waitable"></div>
+
+    <script src="js/jquery-1.6.2-min.js"></script>
+    <script src="js/jquery-ui-1.8.14.custom.min.js"></script>
+	<script>
+		$(document).ready(function() {
+            $('#clicker').click(function() {
+                $(this).text('single clicked');
+            });
+
+            $('#clicker').dblclick(function() {
+                $(this).text('double clicked');
+            });
+
+            $('#clicker').bind('contextmenu', function() {
+                $(this).text('right clicked');
+            });
+
+            $('#focus-blur-detector').focus(function() {
+                $(this).val('focused');
+            });
+
+            $('#focus-blur-detector').blur(function() {
+                $(this).val('blured');
+            });
+
+            $('#mouseover-detector').mouseover(function() {
+                $(this).text('mouse overed');
+            });
+
+            $('.elements input.input.first').keydown(function(ev) {
+                $('.text-event').text('key downed:' + ev.altKey * 1 + ' / ' + ev.ctrlKey * 1 + ' / ' + ev.shiftKey * 1 + ' / ' + ev.metaKey * 1);
+            });
+
+            $('.elements input.input.second').keypress(function(ev) {
+                $('.text-event').text('key pressed:' + ev.which + ' / ' + ev.altKey * 1 + ' / ' + ev.ctrlKey * 1 + ' / ' + ev.shiftKey * 1 + ' / ' + ev.metaKey * 1);
+            });
+
+            $('.elements input.input.third').keyup(function(ev) {
+                $('.text-event').text('key upped:' + ev.which + ' / ' + ev.altKey * 1 + ' / ' + ev.ctrlKey * 1 + ' / ' + ev.shiftKey * 1 + ' / ' + ev.metaKey * 1);
+            });
+
+            $( "#draggable" ).draggable();
+            $( "#droppable" ).droppable({
+                drop: function( event, ui ) {
+                    $( this ).find( "p" ).html( "Dropped!" );
+                }
+            });
+
+            var t1, t2;
+
+            $('#waitable').click(function() {
+                var el = $(this);
+
+                el.html('');
+                clearTimeout(t1);
+                clearTimeout(t2);
+
+                t1 = setTimeout(function() {
+                    el.html('<div>arrived</div>');
+                }, 1000);
+
+                t2 = setTimeout(function() {
+                    el.html('<div>timeout</div>');
+                }, 2000);
+            });
+		});
+	</script>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/json.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/json.php
new file mode 100644
index 0000000..173d358
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/json.php
@@ -0,0 +1,7 @@
+<?php
+
+echo json_encode(array(
+    'key1' => 'val1',
+    'key2' => 234,
+    'key3' => array(1, 2, 3)
+));
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/links.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/links.html
new file mode 100644
index 0000000..ec626bf
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/links.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Links page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <a href="redirector.php">Redirect me to</a>
+    <a href="randomizer.php">Random number page</a>
+    <a href="links.html?quoted">Link with a '</a>
+    <a href="basic_form.html">
+        <img src="basic_form" alt="basic form image"/>
+    </a>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/multi_input_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multi_input_form.html
new file mode 100644
index 0000000..600a500
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multi_input_form.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Multi input Test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Multi input Test</h1>
+
+    <form method="POST" action="advanced_form_post.php">
+        <label>
+            First
+            <input type="text" name="tags[]" value="tag1">
+        </label>
+        <label>
+            Second
+            <input type="text" name="tags[]" value="tag2">
+        </label>
+        <label>
+            Third
+            <input type="text" name="tags[]" value="tag1">
+        </label>
+
+        <input type="submit" name="submit" value="Register" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/multicheckbox_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multicheckbox_form.html
new file mode 100644
index 0000000..a2ae375
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multicheckbox_form.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Multicheckbox Test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Multicheckbox Test</h1>
+
+    <form method="POST" action="advanced_form_post.php">
+        <input type="checkbox" name="mail_types[]" checked="checked" value="update"/>
+        <input type="checkbox" name="mail_types[]" value="spam"/>
+
+        <input type="submit" name="submit" value="Register" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/multiselect_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multiselect_form.html
new file mode 100644
index 0000000..0d28986
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multiselect_form.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Multiselect Test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <h1>Multiselect Test</h1>
+
+    <form method="POST" action="advanced_form_post.php">
+        <select name="select_number">
+            <option value="10">ten</option>
+            <option selected="selected" value="20">twenty</option>
+            <option value="30">thirty</option>
+        </select>
+
+        <select name="select_multiple_numbers[]" multiple="multiple">
+            <option value="1">one</option>
+            <option value="2">two</option>
+            <option value="3">three</option>
+        </select>
+
+        <select name="select_multiple_values[]" multiple="multiple">
+            <option value="1">one</option>
+            <option value="2" selected="selected">two</option>
+            <option value="3" selected="selected">three</option>
+        </select>
+
+        <input type="submit" name="submit" value="Register" />
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup1.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup1.html
new file mode 100644
index 0000000..b8e3fe2
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>popup_1</title>
+</head>
+<body>
+
+    <div id="text">
+        Popup#1 div text
+    </div>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup2.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup2.html
new file mode 100644
index 0000000..dae1932
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup2.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>popup_2</title>
+</head>
+<body>
+
+    <div id="text">
+        Popup#2 div text
+    </div>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/print_cookies.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/print_cookies.php
new file mode 100644
index 0000000..eef496e
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/print_cookies.php
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Cookies page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <?php echo str_replace('>', '', var_export($_COOKIE, true)); ?>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/radio.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/radio.html
new file mode 100644
index 0000000..69a916a
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/radio.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>Radio group</title>
+</head>
+<body>
+    <form id="form1">
+        <input name="group1" type="radio" value="set" id="first" checked="checked">
+        <input name="group1" type="radio" value="updated" id="second">
+
+        <input name="reused" type="radio" value="test" id="reused_form1">
+        <input name="reused" type="radio" value="test2" id="reused2_form1" checked="checked">
+    </form>
+
+    <form id="form2">
+        <input name="reused" type="radio" value="test3" id="reused_form2" checked="checked">
+        <input name="reused" type="radio" value="test4" id="reused2_form2">
+    </form>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/randomizer.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/randomizer.php
new file mode 100644
index 0000000..07a73ec
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/randomizer.php
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Index page</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+
+    <h1 id="number"><?php echo rand() ?></h1>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirect_destination.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirect_destination.html
new file mode 100644
index 0000000..3de7481
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirect_destination.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Redirect destination</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    You were redirected!
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirector.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirector.php
new file mode 100644
index 0000000..44ac8f3
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirector.php
@@ -0,0 +1,3 @@
+<?php
+
+header('location: redirect_destination.html');
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/response_headers.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/response_headers.php
new file mode 100644
index 0000000..a9166b7
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/response_headers.php
@@ -0,0 +1,15 @@
+<?php
+
+header('X-Mink-Test: response-headers');
+
+?>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Response headers</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+    <h1>Response headers</h1>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/session_test.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/session_test.php
new file mode 100644
index 0000000..df1af6f
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/session_test.php
@@ -0,0 +1,18 @@
+<?php
+session_name('_SESS');
+session_start();
+
+if (isset($_GET['login'])) {
+    session_regenerate_id();
+}
+?>
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Session Test</title>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
+</head>
+<body>
+    <div id="session-id"><?php echo session_id() ?></div>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/some_file.txt b/core/vendor/behat/mink/driver-testsuite/web-fixtures/some_file.txt
new file mode 100644
index 0000000..d515a02
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/some_file.txt
@@ -0,0 +1 @@
+1 uploaded file
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page1.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page1.php
new file mode 100644
index 0000000..750249b
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page1.php
@@ -0,0 +1,4 @@
+<?php
+$cookieAtRootPath = false;
+$cookieValue = 'srv_var_is_set_sub_folder';
+require_once __DIR__ . '/../' . basename(__FILE__);
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page2.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page2.php
new file mode 100644
index 0000000..9747fe8
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page2.php
@@ -0,0 +1,2 @@
+<?php
+require_once __DIR__ . '/../' . basename(__FILE__);
diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/window.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/window.html
new file mode 100644
index 0000000..d0a4594
--- /dev/null
+++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/window.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<script>
+function newPopup(url, title) {
+    popupWindow = window.open(
+        url, title,'height=100,width=200,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=yes,menubar=no,location=no,directories=no,status=yes'
+    );
+}
+</script>
+
+<a href="popup1.html" onclick="newPopup(this.href, 'popup_1'); return false;">
+    Popup #1
+</a>
+
+<a href="popup2.html" onclick="newPopup(this.href, 'popup_2'); return false;">
+    Popup #2
+</a>
+
+<div id="text">
+    Main window div text
+</div>
+
+</body>
+</html>
diff --git a/core/vendor/behat/mink/phpdoc.ini.dist b/core/vendor/behat/mink/phpdoc.ini.dist
new file mode 100644
index 0000000..3fef75d
--- /dev/null
+++ b/core/vendor/behat/mink/phpdoc.ini.dist
@@ -0,0 +1,125 @@
+; Default configuration file for PHPDoctor
+
+; This config file will cause PHPDoctor to generate API documentation of
+; itself.
+
+
+; PHPDoctor settings
+; -----------------------------------------------------------------------------
+
+; Names of files to parse. This can be a single filename, or a comma separated
+; list of filenames. Wildcards are allowed.
+
+files = "*.php"
+
+; Names of files or directories to ignore. This can be a single filename, or a
+; comma separated list of filenames. Wildcards are NOT allowed.
+
+ignore = "CVS, .svn, .git, _compiled"
+
+; The directory to look for files in, if not used the PHPDoctor will look in
+; the current directory (the directory it is run from).
+
+source_path = "./src"
+
+; If you do not want PHPDoctor to look in each sub directory for files
+; uncomment this line.
+
+;subdirs = off
+
+; Set how loud PHPDoctor is as it runs. Quiet mode suppresses all output other
+; than warnings and errors. Verbose mode outputs additional messages during
+; execution.
+
+;quiet = on
+;verbose = on
+
+; Select the doclet to use for generating output.
+
+doclet = standard
+;doclet = debug
+
+; The directory to find the doclet in. Doclets are expected to be in a
+; directory named after themselves at the location given.
+
+;doclet_path = ./doclets
+
+; The directory to find taglets in. Taglets allow you to make PHPDoctor handle
+; new tags and to alter the behavour of existing tags and their output.
+
+;taglet_path = ./taglets
+
+; If the code you are parsing does not use package tags or not all elements
+; have package tags, use this setting to place unbound elements into a
+; particular package.
+
+default_package = "Behat\Mink"
+
+; Specifies the name of a HTML file containing text for the overview
+; documentation to be placed on the overview page. The path is relative to
+; "source_path" unless an absolute path is given.
+
+overview = readme.html
+
+; Package comments will be looked for in a file named package.html in the same
+; directory as the first source file parsed in that package or in the directory
+; given below. If package comments are placed in the directory given below then
+; they should be named "<packageName>.html".
+
+package_comment_dir = ./
+
+; Parse out global variables and/or global constants?
+
+;globals = off
+;constants = off
+
+; Generate documentation for all class members
+
+;private = on
+
+; Generate documentation for public and protected class members
+
+;protected = on
+
+; Generate documentation for only public class members
+
+public = on
+
+; Use the PEAR compatible handling of the docblock first sentence
+
+;pear_compat = on
+
+; Standard doclet settings
+; -----------------------------------------------------------------------------
+
+; The directory to place generated documentation in. If the given path is
+; relative to it will be relative to "source_path".
+
+d = "api"
+
+; Specifies the title to be placed in the HTML <title> tag.
+
+windowtitle = "Behat\Mink"
+
+; Specifies the title to be placed near the top of the overview summary file.
+
+doctitle = "Behat\Mink: browser emulators abstraction library for PHP"
+
+; Specifies the header text to be placed at the top of each output file. The
+; header will be placed to the right of the upper navigation bar. 
+
+header = "Behat\Mink"
+
+; Specifies the footer text to be placed at the bottom of each output file. The
+; footer will be placed to the right of the lower navigation bar.
+
+footer = "Behat\Mink"
+
+; Specifies the text to be placed at the bottom of each output file. The text
+; will be placed at the bottom of the page, below the lower navigation bar.
+
+;bottom = "This document was generated by <a href="http://phpdoctor.sourceforge.net/">PHPDoctor: The PHP Documentation Creator</a>"
+
+; Create a class tree?
+
+tree = on
diff --git a/core/vendor/behat/mink/phpunit.xml.dist b/core/vendor/behat/mink/phpunit.xml.dist
new file mode 100644
index 0000000..bf73a69
--- /dev/null
+++ b/core/vendor/behat/mink/phpunit.xml.dist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit colors="true" bootstrap="vendor/autoload.php">
+    <testsuites>
+        <testsuite name="Behat Mink test suite">
+            <directory>tests</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./src/Behat/Mink/</directory>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Driver/CoreDriver.php b/core/vendor/behat/mink/src/Behat/Mink/Driver/CoreDriver.php
new file mode 100644
index 0000000..248f291
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Driver/CoreDriver.php
@@ -0,0 +1,447 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Driver;
+
+use Behat\Mink\Exception\UnsupportedDriverActionException;
+use Behat\Mink\Session;
+
+/**
+ * Core driver.
+ * All other drivers should extend this class for future compatibility.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+abstract class CoreDriver implements DriverInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function setSession(Session $session)
+    {
+        throw new UnsupportedDriverActionException('Setting the session is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start()
+    {
+        throw new UnsupportedDriverActionException('Starting the driver is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isStarted()
+    {
+        throw new UnsupportedDriverActionException('Checking the driver state is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function stop()
+    {
+        throw new UnsupportedDriverActionException('Stopping the driver is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reset()
+    {
+        throw new UnsupportedDriverActionException('Resetting the driver is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function visit($url)
+    {
+        throw new UnsupportedDriverActionException('Visiting an url is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCurrentUrl()
+    {
+        throw new UnsupportedDriverActionException('Getting the current url is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContent()
+    {
+        throw new UnsupportedDriverActionException('Getting the page content is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function find($xpath)
+    {
+        throw new UnsupportedDriverActionException('Finding elements is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTagName($xpath)
+    {
+        throw new UnsupportedDriverActionException('Getting the tag name is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getText($xpath)
+    {
+        throw new UnsupportedDriverActionException('Getting the element text is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getHtml($xpath)
+    {
+        throw new UnsupportedDriverActionException('Getting the element inner HTML is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOuterHtml($xpath)
+    {
+        throw new UnsupportedDriverActionException('Getting the element outer HTML is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttribute($xpath, $name)
+    {
+        throw new UnsupportedDriverActionException('Getting the element attribute is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getValue($xpath)
+    {
+        throw new UnsupportedDriverActionException('Getting the field value is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setValue($xpath, $value)
+    {
+        throw new UnsupportedDriverActionException('Setting the field value is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function check($xpath)
+    {
+        throw new UnsupportedDriverActionException('Checking a checkbox is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function uncheck($xpath)
+    {
+        throw new UnsupportedDriverActionException('Unchecking a checkbox is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isChecked($xpath)
+    {
+        throw new UnsupportedDriverActionException('Getting the state of a checkbox is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function selectOption($xpath, $value, $multiple = false)
+    {
+        throw new UnsupportedDriverActionException('Selecting an option is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function click($xpath)
+    {
+        throw new UnsupportedDriverActionException('Clicking on an element is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function attachFile($xpath, $path)
+    {
+        throw new UnsupportedDriverActionException('Attaching a file in an input is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reload()
+    {
+        throw new UnsupportedDriverActionException('Page reloading is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function forward()
+    {
+        throw new UnsupportedDriverActionException('Forward action is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function back()
+    {
+        throw new UnsupportedDriverActionException('Backward action is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setBasicAuth($user, $password)
+    {
+        throw new UnsupportedDriverActionException('Basic auth setup is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function switchToWindow($name = null)
+    {
+        throw new UnsupportedDriverActionException('Windows management is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function switchToIFrame($name = null)
+    {
+        throw new UnsupportedDriverActionException('iFrames management is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setRequestHeader($name, $value)
+    {
+        throw new UnsupportedDriverActionException('Request headers manipulation is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getResponseHeaders()
+    {
+        throw new UnsupportedDriverActionException('Response headers are not available from %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setCookie($name, $value = null)
+    {
+        throw new UnsupportedDriverActionException('Cookies manipulation is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCookie($name)
+    {
+        throw new UnsupportedDriverActionException('Cookies are not available from %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStatusCode()
+    {
+        throw new UnsupportedDriverActionException('Status code is not available from %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScreenshot()
+    {
+        throw new UnsupportedDriverActionException('Screenshots are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getWindowNames()
+    {
+        throw new UnsupportedDriverActionException('Listing all window names is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getWindowName()
+    {
+        throw new UnsupportedDriverActionException('Listing this window name is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function doubleClick($xpath)
+    {
+        throw new UnsupportedDriverActionException('Double-clicking is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rightClick($xpath)
+    {
+        throw new UnsupportedDriverActionException('Right-clicking is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isVisible($xpath)
+    {
+        throw new UnsupportedDriverActionException('Element visibility check is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isSelected($xpath)
+    {
+        throw new UnsupportedDriverActionException('Element selection check is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function mouseOver($xpath)
+    {
+        throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function focus($xpath)
+    {
+        throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function blur($xpath)
+    {
+        throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function keyPress($xpath, $char, $modifier = null)
+    {
+        throw new UnsupportedDriverActionException('Keyboard manipulations are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function keyDown($xpath, $char, $modifier = null)
+    {
+        throw new UnsupportedDriverActionException('Keyboard manipulations are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function keyUp($xpath, $char, $modifier = null)
+    {
+        throw new UnsupportedDriverActionException('Keyboard manipulations are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dragTo($sourceXpath, $destinationXpath)
+    {
+        throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function executeScript($script)
+    {
+        throw new UnsupportedDriverActionException('JS is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function evaluateScript($script)
+    {
+        throw new UnsupportedDriverActionException('JS is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function wait($timeout, $condition)
+    {
+        throw new UnsupportedDriverActionException('JS is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function resizeWindow($width, $height, $name = null)
+    {
+        throw new UnsupportedDriverActionException('Window resizing is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function maximizeWindow($name = null)
+    {
+        throw new UnsupportedDriverActionException('Window maximize is not supported by %s', $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function submitForm($xpath)
+    {
+        throw new UnsupportedDriverActionException('Form submission is not supported by %s', $this);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Driver/DriverInterface.php b/core/vendor/behat/mink/src/Behat/Mink/Driver/DriverInterface.php
new file mode 100644
index 0000000..831d408
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Driver/DriverInterface.php
@@ -0,0 +1,637 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Driver;
+
+use Behat\Mink\Element\NodeElement;
+use Behat\Mink\Exception\DriverException;
+use Behat\Mink\Exception\UnsupportedDriverActionException;
+use Behat\Mink\Session;
+
+/**
+ * Driver interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface DriverInterface
+{
+    /**
+     * Sets driver's current session.
+     *
+     * @param Session $session
+     */
+    public function setSession(Session $session);
+
+    /**
+     * Starts driver.
+     *
+     * Once started, the driver should be ready to visit a page.
+     *
+     * Calling any action before visiting a page is an undefined behavior.
+     * The only supported method calls on a fresh driver are
+     * - visit()
+     * - setRequestHeader()
+     * - setBasicAuth()
+     * - reset()
+     * - stop()
+     *
+     * Calling start on a started driver is an undefined behavior. Driver
+     * implementations are free to handle it silently or to fail with an
+     * exception.
+     *
+     * @throws DriverException When the driver cannot be started
+     */
+    public function start();
+
+    /**
+     * Checks whether driver is started.
+     *
+     * @return Boolean
+     */
+    public function isStarted();
+
+    /**
+     * Stops driver.
+     *
+     * Once stopped, the driver should be started again before using it again.
+     *
+     * Calling any action on a stopped driver is an undefined behavior.
+     * The only supported method call after stopping a driver is starting it again.
+     *
+     * Calling stop on a stopped driver is an undefined behavior. Driver
+     * implementations are free to handle it silently or to fail with an
+     * exception.
+     *
+     * @throws DriverException When the driver cannot be closed
+     */
+    public function stop();
+
+    /**
+     * Resets driver state.
+     *
+     * This should reset cookies, request headers and basic authentication.
+     * When possible, the history should be reset as well, but this is not enforced
+     * as some implementations may not be able to reset it without restarting the
+     * driver entirely. Consumers requiring a clean history should restart the driver
+     * to enforce it.
+     *
+     * Once reset, the driver should be ready to visit a page.
+     * Calling any action before visiting a page is an undefined behavior.
+     * The only supported method calls on a fresh driver are
+     * - visit()
+     * - setRequestHeader()
+     * - setBasicAuth()
+     * - reset()
+     * - stop()
+     *
+     * Calling reset on a stopped driver is an undefined behavior.
+     */
+    public function reset();
+
+    /**
+     * Visit specified URL.
+     *
+     * @param string $url url of the page
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function visit($url);
+
+    /**
+     * Returns current URL address.
+     *
+     * @return string
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getCurrentUrl();
+
+    /**
+     * Reloads current page.
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function reload();
+
+    /**
+     * Moves browser forward 1 page.
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function forward();
+
+    /**
+     * Moves browser backward 1 page.
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function back();
+
+    /**
+     * Sets HTTP Basic authentication parameters
+     *
+     * @param string|Boolean $user     user name or false to disable authentication
+     * @param string         $password password
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function setBasicAuth($user, $password);
+
+    /**
+     * Switches to specific browser window.
+     *
+     * @param string $name window name (null for switching back to main window)
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function switchToWindow($name = null);
+
+    /**
+     * Switches to specific iFrame.
+     *
+     * @param string $name iframe name (null for switching back)
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function switchToIFrame($name = null);
+
+    /**
+     * Sets specific request header on client.
+     *
+     * @param string $name
+     * @param string $value
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function setRequestHeader($name, $value);
+
+    /**
+     * Returns last response headers.
+     *
+     * @return array
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getResponseHeaders();
+
+    /**
+     * Sets cookie.
+     *
+     * @param string $name
+     * @param string $value
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function setCookie($name, $value = null);
+
+    /**
+     * Returns cookie by name.
+     *
+     * @param string $name
+     *
+     * @return string|null
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getCookie($name);
+
+    /**
+     * Returns last response status code.
+     *
+     * @return integer
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getStatusCode();
+
+    /**
+     * Returns last response content.
+     *
+     * @return string
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getContent();
+
+    /**
+     * Capture a screenshot of the current window.
+     *
+     * @return string screenshot of MIME type image/* depending
+     *                on driver (e.g., image/png, image/jpeg)
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getScreenshot();
+
+    /**
+     * Return the names of all open windows.
+     *
+     * @return array array of all open windows
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getWindowNames();
+
+    /**
+     * Return the name of the currently active window.
+     *
+     * @return string the name of the current window
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getWindowName();
+
+    /**
+     * Finds elements with specified XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return NodeElement[]
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function find($xpath);
+
+    /**
+     * Returns element's tag name by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return string
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getTagName($xpath);
+
+    /**
+     * Returns element's text by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return string
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getText($xpath);
+
+    /**
+     * Returns element's inner html by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return string
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getHtml($xpath);
+
+    /**
+     * Returns element's outer html by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return string
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getOuterHtml($xpath);
+
+    /**
+     * Returns element's attribute by it's XPath query.
+     *
+     * @param string $xpath
+     * @param string $name
+     *
+     * @return string|null
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function getAttribute($xpath, $name);
+
+    /**
+     * Returns element's value by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return string|bool|array
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::getValue
+     */
+    public function getValue($xpath);
+
+    /**
+     * Sets element's value by it's XPath query.
+     *
+     * @param string            $xpath
+     * @param string|bool|array $value
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::setValue
+     */
+    public function setValue($xpath, $value);
+
+    /**
+     * Checks checkbox by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::check
+     */
+    public function check($xpath);
+
+    /**
+     * Unchecks checkbox by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::uncheck
+     */
+    public function uncheck($xpath);
+
+    /**
+     * Checks whether checkbox or radio button located by it's XPath query is checked.
+     *
+     * @param string $xpath
+     *
+     * @return Boolean
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::isChecked
+     */
+    public function isChecked($xpath);
+
+    /**
+     * Selects option from select field or value in radio group located by it's XPath query.
+     *
+     * @param string  $xpath
+     * @param string  $value
+     * @param Boolean $multiple
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::selectOption
+     */
+    public function selectOption($xpath, $value, $multiple = false);
+
+    /**
+     * Checks whether select option, located by it's XPath query, is selected.
+     *
+     * @param string $xpath
+     *
+     * @return Boolean
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::isSelected
+     */
+    public function isSelected($xpath);
+
+    /**
+     * Clicks button or link located by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function click($xpath);
+
+    /**
+     * Double-clicks button or link located by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function doubleClick($xpath);
+
+    /**
+     * Right-clicks button or link located by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function rightClick($xpath);
+
+    /**
+     * Attaches file path to file field located by it's XPath query.
+     *
+     * @param string $xpath
+     * @param string $path
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     *
+     * @see \Behat\Mink\Element\NodeElement::attachFile
+     */
+    public function attachFile($xpath, $path);
+
+    /**
+     * Checks whether element visible located by it's XPath query.
+     *
+     * @param string $xpath
+     *
+     * @return Boolean
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function isVisible($xpath);
+
+    /**
+     * Simulates a mouse over on the element.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function mouseOver($xpath);
+
+    /**
+     * Brings focus to element.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function focus($xpath);
+
+    /**
+     * Removes focus from element.
+     *
+     * @param string $xpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function blur($xpath);
+
+    /**
+     * Presses specific keyboard key.
+     *
+     * @param string         $xpath
+     * @param string|integer $char     could be either char ('b') or char-code (98)
+     * @param string         $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function keyPress($xpath, $char, $modifier = null);
+
+    /**
+     * Pressed down specific keyboard key.
+     *
+     * @param string         $xpath
+     * @param string|integer $char     could be either char ('b') or char-code (98)
+     * @param string         $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function keyDown($xpath, $char, $modifier = null);
+
+    /**
+     * Pressed up specific keyboard key.
+     *
+     * @param string         $xpath
+     * @param string|integer $char     could be either char ('b') or char-code (98)
+     * @param string         $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function keyUp($xpath, $char, $modifier = null);
+
+    /**
+     * Drag one element onto another.
+     *
+     * @param string $sourceXpath
+     * @param string $destinationXpath
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function dragTo($sourceXpath, $destinationXpath);
+
+    /**
+     * Executes JS script.
+     *
+     * @param string $script
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function executeScript($script);
+
+    /**
+     * Evaluates JS script.
+     *
+     * The "return" keyword is optional in the script passed as argument. Driver implementations
+     * must accept the expression both with and without the keyword.
+     *
+     * @param string $script
+     *
+     * @return mixed
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function evaluateScript($script);
+
+    /**
+     * Waits some time or until JS condition turns true.
+     *
+     * @param integer $timeout   timeout in milliseconds
+     * @param string  $condition JS condition
+     *
+     * @return boolean
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function wait($timeout, $condition);
+
+    /**
+     * Set the dimensions of the window.
+     *
+     * @param integer $width  set the window width, measured in pixels
+     * @param integer $height set the window height, measured in pixels
+     * @param string  $name   window name (null for the main window)
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function resizeWindow($width, $height, $name = null);
+
+    /**
+     * Maximize the window if it is not maximized already
+     *
+     * @param string $name window name (null for the main window)
+     *
+     * @throws UnsupportedDriverActionException When operation not supported by the driver
+     * @throws DriverException                  When the operation cannot be done
+     */
+    public function maximizeWindow($name = null);
+
+     /**
+      * Submits the form.
+      *
+      * @param string $xpath Xpath.
+      *
+      * @throws UnsupportedDriverActionException When operation not supported by the driver
+      * @throws DriverException                  When the operation cannot be done
+      *
+      * @see \Behat\Mink\Element\NodeElement::submitForm
+      */
+    public function submitForm($xpath);
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/DocumentElement.php b/core/vendor/behat/mink/src/Behat/Mink/Element/DocumentElement.php
new file mode 100644
index 0000000..dc6bc6e
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Element/DocumentElement.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Element;
+
+/**
+ * Document element.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class DocumentElement extends TraversableElement
+{
+    /**
+     * Returns XPath for handled element.
+     *
+     * @return string
+     */
+    public function getXpath()
+    {
+        return '//html';
+    }
+
+    /**
+     * Returns document content.
+     *
+     * @return string
+     */
+    public function getContent()
+    {
+        return trim($this->getDriver()->getContent());
+    }
+
+    /**
+     * Check whether document has specified content.
+     *
+     * @param string $content
+     *
+     * @return Boolean
+     */
+    public function hasContent($content)
+    {
+        return $this->has('named', array(
+            'content', $this->getSelectorsHandler()->xpathLiteral($content)
+        ));
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/Element.php b/core/vendor/behat/mink/src/Behat/Mink/Element/Element.php
new file mode 100644
index 0000000..07e1e11
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Element/Element.php
@@ -0,0 +1,207 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Element;
+
+use Behat\Mink\Driver\DriverInterface;
+use Behat\Mink\Exception\ElementNotFoundException;
+use Behat\Mink\Selector\SelectorsHandler;
+use Behat\Mink\Selector\Xpath\Manipulator;
+use Behat\Mink\Session;
+
+/**
+ * Base element.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+abstract class Element implements ElementInterface
+{
+    /**
+     * @var Session
+     */
+    private $session;
+
+    /**
+     * Driver.
+     *
+     * @var DriverInterface
+     */
+    private $driver;
+
+    /**
+     * @var SelectorsHandler
+     */
+    private $selectorsHandler;
+
+    /**
+     * @var Manipulator
+     */
+    private $xpathManipulator;
+
+    /**
+     * Initialize element.
+     *
+     * @param Session $session
+     */
+    public function __construct(Session $session)
+    {
+        $this->xpathManipulator = new Manipulator();
+        $this->session = $session;
+
+        $this->driver = $session->getDriver();
+        $this->selectorsHandler = $session->getSelectorsHandler();
+    }
+
+    /**
+     * Returns element session.
+     *
+     * @return Session
+     *
+     * @deprecated Accessing the session from the element is deprecated as of 1.6 and will be impossible in 2.0.
+     */
+    public function getSession()
+    {
+        return $this->session;
+    }
+
+    /**
+     * Returns element's driver.
+     *
+     * @return DriverInterface
+     */
+    protected function getDriver()
+    {
+        return $this->driver;
+    }
+
+    /**
+     * Returns selectors handler.
+     *
+     * @return SelectorsHandler
+     */
+    protected function getSelectorsHandler()
+    {
+        return $this->selectorsHandler;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($selector, $locator)
+    {
+        return null !== $this->find($selector, $locator);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isValid()
+    {
+        return 1 === count($this->getDriver()->find($this->getXpath()));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function waitFor($timeout, $callback)
+    {
+        if (!is_callable($callback)) {
+            throw new \InvalidArgumentException('Given callback is not a valid callable');
+        }
+
+        $start = microtime(true);
+        $end = $start + $timeout;
+
+        do {
+            $result = call_user_func($callback, $this);
+
+            if ($result) {
+                break;
+            }
+
+            usleep(100000);
+        } while (microtime(true) < $end);
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function find($selector, $locator)
+    {
+        $items = $this->findAll($selector, $locator);
+
+        return count($items) ? current($items) : null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function findAll($selector, $locator)
+    {
+        if ('named' === $selector) {
+            $items = $this->findAll('named_exact', $locator);
+            if (empty($items)) {
+                $items = $this->findAll('named_partial', $locator);
+            }
+
+            return $items;
+        }
+
+        $xpath = $this->getSelectorsHandler()->selectorToXpath($selector, $locator);
+        $xpath = $this->xpathManipulator->prepend($xpath, $this->getXpath());
+
+        return $this->getDriver()->find($xpath);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getText()
+    {
+        return $this->getDriver()->getText($this->getXpath());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getHtml()
+    {
+        return $this->getDriver()->getHtml($this->getXpath());
+    }
+
+    /**
+     * Returns element outer html.
+     *
+     * @return string
+     */
+    public function getOuterHtml()
+    {
+        return $this->getDriver()->getOuterHtml($this->getXpath());
+    }
+
+    /**
+     * Builds an ElementNotFoundException
+     *
+     * This is an helper to build the ElementNotFoundException without
+     * needing to use the deprecated getSession accessor in child classes.
+     *
+     * @param string      $type
+     * @param string|null $selector
+     * @param string|null $locator
+     *
+     * @return ElementNotFoundException
+     */
+    protected function elementNotFound($type, $selector = null, $locator = null)
+    {
+        return new ElementNotFoundException($this->session, $type, $selector, $locator);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/ElementInterface.php b/core/vendor/behat/mink/src/Behat/Mink/Element/ElementInterface.php
new file mode 100644
index 0000000..8c134b9
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Element/ElementInterface.php
@@ -0,0 +1,114 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Element;
+
+use Behat\Mink\Session;
+
+/**
+ * Element interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface ElementInterface
+{
+    /**
+     * Returns XPath for handled element.
+     *
+     * @return string
+     */
+    public function getXpath();
+
+    /**
+     * Returns element's session.
+     *
+     * @return Session
+     *
+     * @deprecated Accessing the session from the element is deprecated as of 1.6 and will be impossible in 2.0.
+     */
+    public function getSession();
+
+    /**
+     * Checks whether element with specified selector exists inside the current element.
+     *
+     * @param string       $selector selector engine name
+     * @param string|array $locator  selector locator
+     *
+     * @return Boolean
+     *
+     * @see ElementInterface::findAll for the supported selectors
+     */
+    public function has($selector, $locator);
+
+    /**
+     * Checks if an element still exists in the DOM.
+     *
+     * @return boolean
+     */
+    public function isValid();
+
+    /**
+     * Waits for an element(-s) to appear and returns it.
+     *
+     * @param int|float $timeout  Maximal allowed waiting time in seconds.
+     * @param callable  $callback Callback, which result is both used as waiting condition and returned.
+     *                            Will receive reference to `this element` as first argument.
+     *
+     * @return mixed
+     * @throws \InvalidArgumentException When invalid callback given.
+     */
+    public function waitFor($timeout, $callback);
+
+    /**
+     * Finds first element with specified selector inside the current element.
+     *
+     * @param string       $selector selector engine name
+     * @param string|array $locator  selector locator
+     *
+     * @return NodeElement|null
+     *
+     * @see ElementInterface::findAll for the supported selectors
+     */
+    public function find($selector, $locator);
+
+    /**
+     * Finds all elements with specified selector inside the current element.
+     *
+     * Valid selector engines are named, xpath, css, named_partial and named_exact.
+     *
+     * 'named' is a pseudo selector engine which prefers an exact match but
+     * will return a partial match if no exact match is found.
+     * 'xpath' is a pseudo selector engine supported by SelectorsHandler.
+     *
+     * More selector engines can be registered in the SelectorsHandler.
+     *
+     * @param string       $selector selector engine name
+     * @param string|array $locator  selector locator
+     *
+     * @return NodeElement[]
+     *
+     * @see NamedSelector for the locators supported by the named selectors
+     */
+    public function findAll($selector, $locator);
+
+    /**
+     * Returns element text (inside tag).
+     *
+     * @return string
+     */
+    public function getText();
+
+    /**
+     * Returns element inner html.
+     *
+     * @return string
+     */
+    public function getHtml();
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/NodeElement.php b/core/vendor/behat/mink/src/Behat/Mink/Element/NodeElement.php
new file mode 100644
index 0000000..1f867ed
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Element/NodeElement.php
@@ -0,0 +1,352 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Element;
+
+use Behat\Mink\Session;
+use Behat\Mink\Exception\ElementNotFoundException;
+
+/**
+ * Page element node.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class NodeElement extends TraversableElement
+{
+    private $xpath;
+
+    /**
+     * Initializes node element.
+     *
+     * @param string  $xpath   element xpath
+     * @param Session $session session instance
+     */
+    public function __construct($xpath, Session $session)
+    {
+        $this->xpath = $xpath;
+
+        parent::__construct($session);
+    }
+
+    /**
+     * Returns XPath for handled element.
+     *
+     * @return string
+     */
+    public function getXpath()
+    {
+        return $this->xpath;
+    }
+
+    /**
+     * Returns parent element to the current one.
+     *
+     * @return NodeElement
+     */
+    public function getParent()
+    {
+        return $this->find('xpath', '..');
+    }
+
+    /**
+     * Returns current node tag name.
+     *
+     * The value is always returned in lowercase to allow an easy comparison.
+     *
+     * @return string
+     */
+    public function getTagName()
+    {
+        return strtolower($this->getDriver()->getTagName($this->getXpath()));
+    }
+
+    /**
+     * Returns the value of the form field or option element.
+     *
+     * For checkbox fields, the value is a boolean indicating whether the checkbox is checked.
+     * For radio buttons, the value is the value of the selected button in the radio group
+     *      or null if no button is selected.
+     * For single select boxes, the value is the value of the selected option.
+     * For multiple select boxes, the value is an array of selected option values.
+     * for file inputs, the return value is undefined given that browsers don't allow accessing
+     *      the value of file inputs for security reasons. Some drivers may allow accessing the
+     *      path of the file set in the field, but this is not required if it cannot be implemented.
+     * For textarea elements and all textual fields, the value is the content of the field.
+     * Form option elements, the value is the value of the option (the value attribute or the text
+     *      content if the attribute is not set).
+     *
+     * Calling this method on other elements than form fields or option elements is not allowed.
+     *
+     * @return string|bool|array
+     */
+    public function getValue()
+    {
+        return $this->getDriver()->getValue($this->getXpath());
+    }
+
+    /**
+     * Sets the value of the form field.
+     *
+     * Calling this method on other elements than form fields is not allowed.
+     *
+     * @param string|bool|array $value
+     *
+     * @see NodeElement::getValue for the format of the value for each type of field
+     */
+    public function setValue($value)
+    {
+        $this->getDriver()->setValue($this->getXpath(), $value);
+    }
+
+    /**
+     * Checks whether element has attribute with specified name.
+     *
+     * @param string $name
+     *
+     * @return Boolean
+     */
+    public function hasAttribute($name)
+    {
+        return null !== $this->getDriver()->getAttribute($this->getXpath(), $name);
+    }
+
+    /**
+     * Returns specified attribute value.
+     *
+     * @param string $name
+     *
+     * @return string|null
+     */
+    public function getAttribute($name)
+    {
+        return $this->getDriver()->getAttribute($this->getXpath(), $name);
+    }
+
+    /**
+     * Checks whether an element has a named CSS class
+     *
+     * @param string $className Name of the class
+     *
+     * @return boolean
+     */
+    public function hasClass($className)
+    {
+        if ($this->hasAttribute('class')) {
+            return in_array($className, explode(' ', $this->getAttribute('class')));
+        }
+
+        return false;
+    }
+
+    /**
+     * Clicks current node.
+     */
+    public function click()
+    {
+        $this->getDriver()->click($this->getXpath());
+    }
+
+    /**
+     * Presses current button.
+     */
+    public function press()
+    {
+        $this->click();
+    }
+
+    /**
+     * Double-clicks current node.
+     */
+    public function doubleClick()
+    {
+        $this->getDriver()->doubleClick($this->getXpath());
+    }
+
+    /**
+     * Right-clicks current node.
+     */
+    public function rightClick()
+    {
+        $this->getDriver()->rightClick($this->getXpath());
+    }
+
+    /**
+     * Checks current node if it's a checkbox field.
+     */
+    public function check()
+    {
+        $this->getDriver()->check($this->getXpath());
+    }
+
+    /**
+     * Unchecks current node if it's a checkbox field.
+     */
+    public function uncheck()
+    {
+        $this->getDriver()->uncheck($this->getXpath());
+    }
+
+    /**
+     * Checks whether current node is checked if it's a checkbox or radio field.
+     *
+     * Calling this method on any other elements is not allowed.
+     *
+     * @return Boolean
+     */
+    public function isChecked()
+    {
+        return (Boolean) $this->getDriver()->isChecked($this->getXpath());
+    }
+
+    /**
+     * Selects specified option for select field or specified radio button in the group
+     *
+     * If the current node is a select box, this selects the option found by its value or
+     * its text.
+     * If the current node is a radio button, this selects the radio button with the given
+     * value in the radio button group of the current node.
+     *
+     * Calling this method on any other elements is not allowed.
+     *
+     * @param string  $option
+     * @param Boolean $multiple whether the option should be added to the selection for multiple selects
+     *
+     * @throws ElementNotFoundException when the option is not found in the select box
+     */
+    public function selectOption($option, $multiple = false)
+    {
+        if ('select' !== $this->getTagName()) {
+            $this->getDriver()->selectOption($this->getXpath(), $option, $multiple);
+
+            return;
+        }
+
+        $opt = $this->find('named', array(
+            'option', $this->getSelectorsHandler()->xpathLiteral($option)
+        ));
+
+        if (null === $opt) {
+            throw $this->elementNotFound('select option', 'value|text', $option);
+        }
+
+        $this->getDriver()->selectOption($this->getXpath(), $opt->getValue(), $multiple);
+    }
+
+    /**
+     * Checks whether current node is selected if it's a option field.
+     *
+     * Calling this method on any other elements is not allowed.
+     *
+     * @return Boolean
+     */
+    public function isSelected()
+    {
+        return (Boolean) $this->getDriver()->isSelected($this->getXpath());
+    }
+
+    /**
+     * Attach file to current node if it's a file input.
+     *
+     * Calling this method on any other elements than file input is not allowed.
+     *
+     * @param string $path path to file (local)
+     */
+    public function attachFile($path)
+    {
+        $this->getDriver()->attachFile($this->getXpath(), $path);
+    }
+
+    /**
+     * Checks whether current node is visible on page.
+     *
+     * @return Boolean
+     */
+    public function isVisible()
+    {
+        return (Boolean) $this->getDriver()->isVisible($this->getXpath());
+    }
+
+    /**
+     * Simulates a mouse over on the element.
+     */
+    public function mouseOver()
+    {
+        $this->getDriver()->mouseOver($this->getXpath());
+    }
+
+    /**
+     * Drags current node onto other node.
+     *
+     * @param ElementInterface $destination other node
+     */
+    public function dragTo(ElementInterface $destination)
+    {
+        $this->getDriver()->dragTo($this->getXpath(), $destination->getXpath());
+    }
+
+    /**
+     * Brings focus to element.
+     */
+    public function focus()
+    {
+        $this->getDriver()->focus($this->getXpath());
+    }
+
+    /**
+     * Removes focus from element.
+     */
+    public function blur()
+    {
+        $this->getDriver()->blur($this->getXpath());
+    }
+
+    /**
+     * Presses specific keyboard key.
+     *
+     * @param string|integer $char     could be either char ('b') or char-code (98)
+     * @param string         $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
+     */
+    public function keyPress($char, $modifier = null)
+    {
+        $this->getDriver()->keyPress($this->getXpath(), $char, $modifier);
+    }
+
+    /**
+     * Pressed down specific keyboard key.
+     *
+     * @param string|integer $char     could be either char ('b') or char-code (98)
+     * @param string         $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
+     */
+    public function keyDown($char, $modifier = null)
+    {
+        $this->getDriver()->keyDown($this->getXpath(), $char, $modifier);
+    }
+
+    /**
+     * Pressed up specific keyboard key.
+     *
+     * @param string|integer $char     could be either char ('b') or char-code (98)
+     * @param string         $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
+     */
+    public function keyUp($char, $modifier = null)
+    {
+        $this->getDriver()->keyUp($this->getXpath(), $char, $modifier);
+    }
+
+    /**
+     * Submits the form.
+     *
+     * Calling this method on anything else than form elements is not allowed.
+     */
+    public function submit()
+    {
+        $this->getDriver()->submitForm($this->getXpath());
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/TraversableElement.php b/core/vendor/behat/mink/src/Behat/Mink/Element/TraversableElement.php
new file mode 100644
index 0000000..27843d2
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Element/TraversableElement.php
@@ -0,0 +1,309 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Element;
+
+use Behat\Mink\Exception\ElementNotFoundException;
+
+/**
+ * Traversable element.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+abstract class TraversableElement extends Element
+{
+    /**
+     * Finds element by its id.
+     *
+     * @param string $id element id
+     *
+     * @return NodeElement|null
+     */
+    public function findById($id)
+    {
+        $id = $this->getSelectorsHandler()->xpathLiteral($id);
+
+        return $this->find('named', array('id', $id));
+    }
+
+    /**
+     * Checks whether element has a link with specified locator.
+     *
+     * @param string $locator link id, title, text or image alt
+     *
+     * @return Boolean
+     */
+    public function hasLink($locator)
+    {
+        return null !== $this->findLink($locator);
+    }
+
+    /**
+     * Finds link with specified locator.
+     *
+     * @param string $locator link id, title, text or image alt
+     *
+     * @return NodeElement|null
+     */
+    public function findLink($locator)
+    {
+        return $this->find('named', array(
+            'link', $this->getSelectorsHandler()->xpathLiteral($locator)
+        ));
+    }
+
+    /**
+     * Clicks link with specified locator.
+     *
+     * @param string $locator link id, title, text or image alt
+     *
+     * @throws ElementNotFoundException
+     */
+    public function clickLink($locator)
+    {
+        $link = $this->findLink($locator);
+
+        if (null === $link) {
+            throw $this->elementNotFound('link', 'id|title|alt|text', $locator);
+        }
+
+        $link->click();
+    }
+
+    /**
+     * Checks whether element has a button (input[type=submit|image|button|reset], button) with specified locator.
+     *
+     * @param string $locator button id, value or alt
+     *
+     * @return Boolean
+     */
+    public function hasButton($locator)
+    {
+        return null !== $this->findButton($locator);
+    }
+
+    /**
+     * Finds button (input[type=submit|image|button|reset], button) with specified locator.
+     *
+     * @param string $locator button id, value or alt
+     *
+     * @return NodeElement|null
+     */
+    public function findButton($locator)
+    {
+        return $this->find('named', array(
+            'button', $this->getSelectorsHandler()->xpathLiteral($locator)
+        ));
+    }
+
+    /**
+     * Presses button (input[type=submit|image|button|reset], button) with specified locator.
+     *
+     * @param string $locator button id, value or alt
+     *
+     * @throws ElementNotFoundException
+     */
+    public function pressButton($locator)
+    {
+        $button = $this->findButton($locator);
+
+        if (null === $button) {
+            throw $this->elementNotFound('button', 'id|name|title|alt|value', $locator);
+        }
+
+        $button->press();
+    }
+
+    /**
+     * Checks whether element has a field (input, textarea, select) with specified locator.
+     *
+     * @param string $locator input id, name or label
+     *
+     * @return Boolean
+     */
+    public function hasField($locator)
+    {
+        return null !== $this->findField($locator);
+    }
+
+    /**
+     * Finds field (input, textarea, select) with specified locator.
+     *
+     * @param string $locator input id, name or label
+     *
+     * @return NodeElement|null
+     */
+    public function findField($locator)
+    {
+        return $this->find('named', array(
+            'field', $this->getSelectorsHandler()->xpathLiteral($locator)
+        ));
+    }
+
+    /**
+     * Fills in field (input, textarea, select) with specified locator.
+     *
+     * @param string $locator input id, name or label
+     * @param string $value   value
+     *
+     * @throws ElementNotFoundException
+     *
+     * @see NodeElement::setValue
+     */
+    public function fillField($locator, $value)
+    {
+        $field = $this->findField($locator);
+
+        if (null === $field) {
+            throw $this->elementNotFound('form field', 'id|name|label|value', $locator);
+        }
+
+        $field->setValue($value);
+    }
+
+    /**
+     * Checks whether element has a checkbox with specified locator, which is checked.
+     *
+     * @param string $locator input id, name or label
+     *
+     * @return Boolean
+     *
+     * @see NodeElement::isChecked
+     */
+    public function hasCheckedField($locator)
+    {
+        $field = $this->findField($locator);
+
+        return null !== $field && $field->isChecked();
+    }
+
+    /**
+     * Checks whether element has a checkbox with specified locator, which is unchecked.
+     *
+     * @param string $locator input id, name or label
+     *
+     * @return Boolean
+     *
+     * @see NodeElement::isChecked
+     */
+    public function hasUncheckedField($locator)
+    {
+        $field = $this->findField($locator);
+
+        return null !== $field && !$field->isChecked();
+    }
+
+    /**
+     * Checks checkbox with specified locator.
+     *
+     * @param string $locator input id, name or label
+     *
+     * @throws ElementNotFoundException
+     */
+    public function checkField($locator)
+    {
+        $field = $this->findField($locator);
+
+        if (null === $field) {
+            throw $this->elementNotFound('form field', 'id|name|label|value', $locator);
+        }
+
+        $field->check();
+    }
+
+    /**
+     * Unchecks checkbox with specified locator.
+     *
+     * @param string $locator input id, name or label
+     *
+     * @throws ElementNotFoundException
+     */
+    public function uncheckField($locator)
+    {
+        $field = $this->findField($locator);
+
+        if (null === $field) {
+            throw $this->elementNotFound('form field', 'id|name|label|value', $locator);
+        }
+
+        $field->uncheck();
+    }
+
+    /**
+     * Checks whether element has a select field with specified locator.
+     *
+     * @param string $locator select id, name or label
+     *
+     * @return Boolean
+     */
+    public function hasSelect($locator)
+    {
+        return $this->has('named', array(
+            'select', $this->getSelectorsHandler()->xpathLiteral($locator)
+        ));
+    }
+
+    /**
+     * Selects option from select field with specified locator.
+     *
+     * @param string  $locator  input id, name or label
+     * @param string  $value    option value
+     * @param Boolean $multiple select multiple options
+     *
+     * @throws ElementNotFoundException
+     *
+     * @see NodeElement::selectOption
+     */
+    public function selectFieldOption($locator, $value, $multiple = false)
+    {
+        $field = $this->findField($locator);
+
+        if (null === $field) {
+            throw $this->elementNotFound('form field', 'id|name|label|value', $locator);
+        }
+
+        $field->selectOption($value, $multiple);
+    }
+
+    /**
+     * Checks whether element has a table with specified locator.
+     *
+     * @param string $locator table id or caption
+     *
+     * @return Boolean
+     */
+    public function hasTable($locator)
+    {
+        return $this->has('named', array(
+            'table', $this->getSelectorsHandler()->xpathLiteral($locator)
+        ));
+    }
+
+    /**
+     * Attach file to file field with specified locator.
+     *
+     * @param string $locator input id, name or label
+     * @param string $path    path to file
+     *
+     * @throws ElementNotFoundException
+     *
+     * @see NodeElement::attachFile
+     */
+    public function attachFileToField($locator, $path)
+    {
+        $field = $this->findField($locator);
+
+        if (null === $field) {
+            throw $this->elementNotFound('form field', 'id|name|label|value', $locator);
+        }
+
+        $field->attachFile($path);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/DriverException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/DriverException.php
new file mode 100644
index 0000000..840f8bd
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/DriverException.php
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+/**
+ * Exception thrown by drivers when they fail to perform an action.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class DriverException extends Exception
+{
+    /**
+     * Initializes exception.
+     *
+     * @param string          $message
+     * @param int             $code
+     * @param \Exception|null $previous
+     */
+    public function __construct($message, $code = 0, \Exception $previous = null)
+    {
+        parent::__construct($message, $code, $previous);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementException.php
new file mode 100644
index 0000000..49d5c6c
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementException.php
@@ -0,0 +1,58 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+use Behat\Mink\Element\Element;
+
+/**
+ * A standard way for elements to re-throw exceptions
+ *
+ * @deprecated This exception class is not used anymore in Mink 1.6 and will be removed in 2.0
+ *
+ * @author Chris Worfolk <xmeltrut@gmail.com>
+ */
+class ElementException extends Exception
+{
+    private $element;
+
+    /**
+     * Initialises exception.
+     *
+     * @param Element    $element   optional message
+     * @param \Exception $exception exception
+     */
+    public function __construct(Element $element, \Exception $exception)
+    {
+        $this->element = $element;
+
+        parent::__construct(sprintf("Exception thrown by %s\n%s", $element->getXpath(), $exception->getMessage()));
+    }
+
+    /**
+     * Override default toString so we don't send a full backtrace in verbose mode.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->getMessage();
+    }
+
+    /**
+     * Get the element that caused the exception
+     *
+     * @return Element
+     */
+    public function getElement()
+    {
+        return $this->element;
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementHtmlException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementHtmlException.php
new file mode 100644
index 0000000..99e2ea6
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementHtmlException.php
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+use Behat\Mink\Session;
+use Behat\Mink\Element\Element;
+
+/**
+ * Exception thrown when an expectation on the HTML of an element fails.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ElementHtmlException extends ExpectationException
+{
+    /**
+     * Element instance.
+     *
+     * @var Element
+     */
+    protected $element;
+
+    /**
+     * Initializes exception.
+     *
+     * @param string     $message   optional message
+     * @param Session    $session   session instance
+     * @param Element    $element   element
+     * @param \Exception $exception expectation exception
+     */
+    public function __construct($message, Session $session, Element $element, \Exception $exception = null)
+    {
+        $this->element = $element;
+
+        parent::__construct($message, $session, $exception);
+    }
+
+    protected function getContext()
+    {
+        return $this->element->getOuterHtml();
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementNotFoundException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementNotFoundException.php
new file mode 100644
index 0000000..2d379a8
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementNotFoundException.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+use Behat\Mink\Session;
+
+/**
+ * Exception thrown when an expected element is not found.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ElementNotFoundException extends ExpectationException
+{
+    /**
+     * Initializes exception.
+     *
+     * @param Session $session  session instance
+     * @param string  $type     element type
+     * @param string  $selector element selector type
+     * @param string  $locator  element locator
+     */
+    public function __construct(Session $session, $type = null, $selector = null, $locator = null)
+    {
+        $message = '';
+
+        if (null !== $type) {
+            $message .= ucfirst($type);
+        } else {
+            $message .= 'Tag';
+        }
+
+        if (null !== $locator) {
+            if (null === $selector || in_array($selector, array('css', 'xpath'))) {
+                $selector = 'matching '.($selector ?: 'locator');
+            } else {
+                $selector = 'with '.$selector;
+            }
+            $message .= ' '.$selector.' "' . $locator . '"';
+        }
+
+        $message .= ' not found.';
+
+        parent::__construct($message, $session);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementTextException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementTextException.php
new file mode 100644
index 0000000..5efff0f
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementTextException.php
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+/**
+ * Exception thrown when an expectation on the text of an element fails.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ElementTextException extends ElementHtmlException
+{
+    protected function getContext()
+    {
+        return $this->element->getText();
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/Exception.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/Exception.php
new file mode 100644
index 0000000..4662a5b
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/Exception.php
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+/**
+ * Mink base exception class.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+abstract class Exception extends \Exception
+{
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ExpectationException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ExpectationException.php
new file mode 100644
index 0000000..da1723c
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ExpectationException.php
@@ -0,0 +1,145 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+use Behat\Mink\Session;
+
+/**
+ * Exception thrown for failed expectations.
+ *
+ * Some specialized child classes are available to customize the error rendering.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ExpectationException extends Exception
+{
+    private $session;
+
+    /**
+     * Initializes exception.
+     *
+     * @param string     $message   optional message
+     * @param Session    $session   session instance
+     * @param \Exception $exception expectation exception
+     */
+    public function __construct($message, Session $session, \Exception $exception = null)
+    {
+        $this->session = $session;
+
+        if (!$message && null !== $exception) {
+            $message = $exception->getMessage();
+        }
+
+        parent::__construct($message, 0, $exception);
+    }
+
+    /**
+     * Returns exception message with additional context info.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        try {
+            $pageText = $this->pipeString($this->trimString($this->getContext()) . "\n");
+            $string   = sprintf("%s\n\n%s%s", $this->getMessage(), $this->getResponseInfo(), $pageText);
+        } catch (\Exception $e) {
+            return $this->getMessage();
+        }
+
+        return $string;
+    }
+
+    /**
+     * Gets the context rendered for this exception
+     *
+     * @return string
+     */
+    protected function getContext()
+    {
+        return $this->trimBody($this->getSession()->getPage()->getContent());
+    }
+
+    /**
+     * Returns exception session.
+     *
+     * @return Session
+     */
+    protected function getSession()
+    {
+        return $this->session;
+    }
+
+    /**
+     * Prepends every line in a string with pipe (|).
+     *
+     * @param string $string
+     *
+     * @return string
+     */
+    protected function pipeString($string)
+    {
+        return '|  ' . strtr($string, array("\n" => "\n|  "));
+    }
+
+    /**
+     * Removes response header/footer, letting only <body /> content.
+     *
+     * @param string $string response content
+     *
+     * @return string
+     */
+    protected function trimBody($string)
+    {
+        $string = preg_replace(array('/^.*<body>/s', '/<\/body>.*$/s'), array('<body>', '</body>'), $string);
+
+        return $string;
+    }
+
+    /**
+     * Trims string to specified number of chars.
+     *
+     * @param string  $string response content
+     * @param integer $count  trim count
+     *
+     * @return string
+     */
+    protected function trimString($string, $count = 1000)
+    {
+        $string = trim($string);
+
+        if ($count < mb_strlen($string)) {
+            return mb_substr($string, 0, $count - 3) . '...';
+        }
+
+        return $string;
+    }
+
+    /**
+     * Returns response information string.
+     *
+     * @return string
+     */
+    protected function getResponseInfo()
+    {
+        $driver = basename(str_replace('\\', '/', get_class($this->session->getDriver())));
+
+        $info = '+--[ ';
+        try {
+            $info .= 'HTTP/1.1 '.$this->session->getStatusCode().' | ';
+        } catch (UnsupportedDriverActionException $e) {
+            // Ignore the status code when not supported
+        }
+        $info .= $this->session->getCurrentUrl().' | '.$driver." ]\n|\n";
+
+        return $info;
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ResponseTextException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ResponseTextException.php
new file mode 100644
index 0000000..145ed6d
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ResponseTextException.php
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+/**
+ * Exception thrown when an expectation on the response text fails.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ResponseTextException extends ExpectationException
+{
+    protected function getContext()
+    {
+        return $this->getSession()->getPage()->getText();
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/UnsupportedDriverActionException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/UnsupportedDriverActionException.php
new file mode 100644
index 0000000..d02969b
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/UnsupportedDriverActionException.php
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Exception;
+
+use Behat\Mink\Driver\DriverInterface;
+
+/**
+ * Exception thrown by drivers when they don't support the requested action.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class UnsupportedDriverActionException extends DriverException
+{
+    /**
+     * Initializes exception.
+     *
+     * @param string          $template what is unsupported?
+     * @param DriverInterface $driver   driver instance
+     * @param \Exception      $previous previous exception
+     */
+    public function __construct($template, DriverInterface $driver, \Exception $previous = null)
+    {
+        $message = sprintf($template, get_class($driver));
+
+        parent::__construct($message, 0, $previous);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Mink.php b/core/vendor/behat/mink/src/Behat/Mink/Mink.php
new file mode 100644
index 0000000..d1621b1
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Mink.php
@@ -0,0 +1,216 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink;
+
+/**
+ * Mink sessions manager.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Mink
+{
+    private $defaultSessionName;
+
+    /**
+     * Sessions.
+     *
+     * @var Session[]
+     */
+    private $sessions = array();
+
+    /**
+     * Initializes manager.
+     *
+     * @param Session[] $sessions
+     */
+    public function __construct(array $sessions = array())
+    {
+        foreach ($sessions as $name => $session) {
+            $this->registerSession($name, $session);
+        }
+    }
+
+    /**
+     * Stops all started sessions.
+     */
+    public function __destruct()
+    {
+        $this->stopSessions();
+    }
+
+    /**
+     * Registers new session.
+     *
+     * @param string  $name
+     * @param Session $session
+     */
+    public function registerSession($name, Session $session)
+    {
+        $name = strtolower($name);
+
+        $this->sessions[$name] = $session;
+    }
+
+    /**
+     * Checks whether session with specified name is registered.
+     *
+     * @param string $name
+     *
+     * @return Boolean
+     */
+    public function hasSession($name)
+    {
+        return isset($this->sessions[strtolower($name)]);
+    }
+
+    /**
+     * Sets default session name to use.
+     *
+     * @param string $name name of the registered session
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setDefaultSessionName($name)
+    {
+        $name = strtolower($name);
+
+        if (!isset($this->sessions[$name])) {
+            throw new \InvalidArgumentException(sprintf('Session "%s" is not registered.', $name));
+        }
+
+        $this->defaultSessionName = $name;
+    }
+
+    /**
+     * Returns default session name or null if none.
+     *
+     * @return null|string
+     */
+    public function getDefaultSessionName()
+    {
+        return $this->defaultSessionName;
+    }
+
+    /**
+     * Returns registered session by it's name or active one and automatically starts it if required.
+     *
+     * @param string $name session name
+     *
+     * @return Session
+     *
+     * @throws \InvalidArgumentException If the named session is not registered
+     */
+    public function getSession($name = null)
+    {
+        $session = $this->locateSession($name);
+
+        // start session if needed
+        if (!$session->isStarted()) {
+            $session->start();
+        }
+
+        return $session;
+    }
+
+    /**
+     * Checks whether a named session (or the default session) has already been started
+     *
+     * @param string $name session name - if null then the default session will be checked
+     *
+     * @return bool whether the session has been started
+     *
+     * @throws \InvalidArgumentException If the named session is not registered
+     */
+    public function isSessionStarted($name = null)
+    {
+        $session = $this->locateSession($name);
+
+        return $session->isStarted();
+    }
+
+    /**
+     * Returns session asserter.
+     *
+     * @param Session|string $session session object or name
+     *
+     * @return WebAssert
+     */
+    public function assertSession($session = null)
+    {
+        if (!($session instanceof Session)) {
+            $session = $this->getSession($session);
+        }
+
+        return new WebAssert($session);
+    }
+
+    /**
+     * Resets all started sessions.
+     */
+    public function resetSessions()
+    {
+        foreach ($this->sessions as $session) {
+            if ($session->isStarted()) {
+                $session->reset();
+            }
+        }
+    }
+
+    /**
+     * Restarts all started sessions.
+     */
+    public function restartSessions()
+    {
+        foreach ($this->sessions as $session) {
+            if ($session->isStarted()) {
+                $session->restart();
+            }
+        }
+    }
+
+    /**
+     * Stops all started sessions.
+     */
+    public function stopSessions()
+    {
+        foreach ($this->sessions as $session) {
+            if ($session->isStarted()) {
+                $session->stop();
+            }
+        }
+    }
+
+    /**
+     * Returns the named or default session without starting it.
+     *
+     * @param string $name session name
+     *
+     * @return Session
+     *
+     * @throws \InvalidArgumentException If the named session is not registered
+     */
+    protected function locateSession($name = null)
+    {
+        $name = strtolower($name) ?: $this->defaultSessionName;
+
+        if (null === $name) {
+            throw new \InvalidArgumentException('Specify session name to get');
+        }
+
+        if (!isset($this->sessions[$name])) {
+            throw new \InvalidArgumentException(sprintf('Session "%s" is not registered.', $name));
+        }
+
+        $session = $this->sessions[$name];
+
+        return $session;
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/CssSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/CssSelector.php
new file mode 100644
index 0000000..3636586
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/CssSelector.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector;
+
+use Symfony\Component\CssSelector\CssSelector as CSS;
+
+/**
+ * CSS selector engine. Transforms CSS to XPath.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CssSelector implements SelectorInterface
+{
+    /**
+     * Translates CSS into XPath.
+     *
+     * @param string|array $locator current selector locator
+     *
+     * @return string
+     */
+    public function translateToXPath($locator)
+    {
+        if (!is_string($locator)) {
+            throw new \InvalidArgumentException('The CssSelector expects to get a string as locator');
+        }
+
+        return CSS::toXPath($locator);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/ExactNamedSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/ExactNamedSelector.php
new file mode 100644
index 0000000..03315a9
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/ExactNamedSelector.php
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector;
+
+/**
+ * Exact match selector engine. Like the Named selector engine but ignores partial matches.
+ */
+class ExactNamedSelector extends NamedSelector
+{
+    public function __construct()
+    {
+        $this->registerReplacement('%tagTextMatch%', 'normalize-space(string(.)) = %locator%');
+        $this->registerReplacement('%valueMatch%', './@value = %locator%');
+        $this->registerReplacement('%titleMatch%', './@title = %locator%');
+        $this->registerReplacement('%altMatch%', './@alt = %locator%');
+        $this->registerReplacement('%relMatch%', './@rel = %locator%');
+        $this->registerReplacement('%labelAttributeMatch%', './@label = %locator%');
+
+        parent::__construct();
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/NamedSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/NamedSelector.php
new file mode 100644
index 0000000..4845f99
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/NamedSelector.php
@@ -0,0 +1,238 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector;
+
+/**
+ * Named selectors engine. Uses registered XPath selectors to create new expressions.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class NamedSelector implements SelectorInterface
+{
+    private $replacements = array(
+        // simple replacements
+        '%lowercaseType%' => "translate(./@type, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')",
+        '%lowercaseRole%' => "translate(./@role, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')",
+        '%tagTextMatch%' => 'contains(normalize-space(string(.)), %locator%)',
+        '%labelTextMatch%' => './@id = //label[%tagTextMatch%]/@for',
+        '%idMatch%' => './@id = %locator%',
+        '%valueMatch%' => 'contains(./@value, %locator%)',
+        '%idOrValueMatch%' => '(%idMatch% or %valueMatch%)',
+        '%idOrNameMatch%' => '(%idMatch% or ./@name = %locator%)',
+        '%placeholderMatch%' => './@placeholder = %locator%',
+        '%titleMatch%' => 'contains(./@title, %locator%)',
+        '%altMatch%' => 'contains(./@alt, %locator%)',
+        '%relMatch%' => 'contains(./@rel, %locator%)',
+        '%labelAttributeMatch%' => 'contains(./@label, %locator%)',
+
+        // complex replacements
+        '%inputTypeWithoutPlaceholderFilter%' => "%lowercaseType% = 'radio' or %lowercaseType% = 'checkbox' or %lowercaseType% = 'file'",
+        '%fieldFilterWithPlaceholder%' => 'self::input[not(%inputTypeWithoutPlaceholderFilter%)] | self::textarea',
+        '%fieldMatchWithPlaceholder%' => '(%idOrNameMatch% or %labelTextMatch% or %placeholderMatch%)',
+        '%fieldMatchWithoutPlaceholder%' => '(%idOrNameMatch% or %labelTextMatch%)',
+        '%fieldFilterWithoutPlaceholder%' => 'self::input[%inputTypeWithoutPlaceholderFilter%] | self::select',
+        '%buttonTypeFilter%' => "%lowercaseType% = 'submit' or %lowercaseType% = 'image' or %lowercaseType% = 'button' or %lowercaseType% = 'reset'",
+        '%notFieldTypeFilter%' => "not(%buttonTypeFilter% or %lowercaseType% = 'hidden')",
+        '%buttonMatch%' => '%idOrNameMatch% or %valueMatch% or %titleMatch%',
+        '%linkMatch%' => '(%idMatch% or %tagTextMatch% or %titleMatch% or %relMatch%)',
+        '%imgAltMatch%' => './/img[%altMatch%]',
+    );
+
+    private $selectors = array(
+        'fieldset' => <<<XPATH
+.//fieldset
+[(%idMatch% or .//legend[%tagTextMatch%])]
+XPATH
+
+        ,'field' => <<<XPATH
+.//*
+[%fieldFilterWithPlaceholder%][%notFieldTypeFilter%][%fieldMatchWithPlaceholder%]
+|
+.//label[%tagTextMatch%]//.//*[%fieldFilterWithPlaceholder%][%notFieldTypeFilter%]
+|
+.//*
+[%fieldFilterWithoutPlaceholder%][%notFieldTypeFilter%][%fieldMatchWithoutPlaceholder%]
+|
+.//label[%tagTextMatch%]//.//*[%fieldFilterWithoutPlaceholder%][%notFieldTypeFilter%]
+XPATH
+
+        ,'link' => <<<XPATH
+.//a
+[./@href][(%linkMatch% or %imgAltMatch%)]
+|
+.//*
+[%lowercaseRole% = 'link'][(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)]
+XPATH
+
+        ,'button' => <<<XPATH
+.//input
+[%buttonTypeFilter%][(%buttonMatch%)]
+|
+.//input
+[%lowercaseType% = 'image'][%altMatch%]
+|
+.//button
+[(%buttonMatch% or %tagTextMatch%)]
+|
+.//*
+[%lowercaseRole% = 'button'][(%buttonMatch% or %tagTextMatch%)]
+XPATH
+
+        ,'link_or_button' => <<<XPATH
+.//a
+[./@href][(%linkMatch% or %imgAltMatch%)]
+|
+.//input
+[%buttonTypeFilter%][(%idOrValueMatch% or %titleMatch%)]
+|
+.//input
+[%lowercaseType% = 'image'][%altMatch%]
+|
+.//button
+[(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)]
+|
+.//*
+[(%lowercaseRole% = 'button' or %lowercaseRole% = 'link')][(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)]
+XPATH
+
+        ,'content' => <<<XPATH
+./descendant-or-self::*
+[%tagTextMatch%]
+XPATH
+
+        ,'select' => <<<XPATH
+.//select
+[%fieldMatchWithoutPlaceholder%]
+|
+.//label[%tagTextMatch%]//.//select
+XPATH
+
+        ,'checkbox' => <<<XPATH
+.//input
+[%lowercaseType% = 'checkbox'][%fieldMatchWithoutPlaceholder%]
+|
+.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'checkbox']
+XPATH
+
+        ,'radio' => <<<XPATH
+.//input
+[%lowercaseType% = 'radio'][%fieldMatchWithoutPlaceholder%]
+|
+.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'radio']
+XPATH
+
+        ,'file' => <<<XPATH
+.//input
+[%lowercaseType% = 'file'][%fieldMatchWithoutPlaceholder%]
+|
+.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'file']
+XPATH
+
+        ,'optgroup' => <<<XPATH
+.//optgroup
+[%labelAttributeMatch%]
+XPATH
+
+        ,'option' => <<<XPATH
+.//option
+[(./@value = %locator% or %tagTextMatch%)]
+XPATH
+
+        ,'table' => <<<XPATH
+.//table
+[(%idMatch% or .//caption[%tagTextMatch%])]
+XPATH
+        ,'id' => <<<XPATH
+.//*[%idMatch%]
+XPATH
+    ,'id_or_name' => <<<XPATH
+.//*[%idOrNameMatch%]
+XPATH
+    );
+
+    /**
+     * Creates selector instance.
+     */
+    public function __construct()
+    {
+        foreach ($this->replacements as $from => $to) {
+            $this->replacements[$from] = strtr($to, $this->replacements);
+        }
+
+        foreach ($this->selectors as $alias => $selector) {
+            $this->selectors[$alias] = strtr($selector, $this->replacements);
+        }
+    }
+
+    /**
+     * Registers new XPath selector with specified name.
+     *
+     * @param string $name  name for selector
+     * @param string $xpath xpath expression
+     */
+    public function registerNamedXpath($name, $xpath)
+    {
+        $this->selectors[$name] = $xpath;
+    }
+
+    /**
+     * Translates provided locator into XPath.
+     *
+     * @param string|array $locator selector name or array of (selector_name, locator)
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function translateToXPath($locator)
+    {
+        if (2 < count($locator)) {
+            throw new \InvalidArgumentException('NamedSelector expects array(name, locator) as argument');
+        }
+
+        if (2 == count($locator)) {
+            $selector   = $locator[0];
+            $locator    = $locator[1];
+        } else {
+            $selector   = (string) $locator;
+            $locator    = null;
+        }
+
+        if (!isset($this->selectors[$selector])) {
+            throw new \InvalidArgumentException(sprintf(
+                'Unknown named selector provided: "%s". Expected one of (%s)',
+                $selector,
+                implode(', ', array_keys($this->selectors))
+            ));
+        }
+
+        $xpath = $this->selectors[$selector];
+
+        if (null !== $locator) {
+            $xpath = strtr($xpath, array('%locator%' => $locator));
+        }
+
+        return $xpath;
+    }
+
+    /**
+     * Registers a replacement in the list of replacements
+     *
+     * This method must be called in the constructor before calling the parent constructor.
+     *
+     * @param string $from
+     * @param string $to
+     */
+    protected function registerReplacement($from, $to)
+    {
+        $this->replacements[$from] = $to;
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/PartialNamedSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/PartialNamedSelector.php
new file mode 100644
index 0000000..b864a22
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/PartialNamedSelector.php
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector;
+
+/**
+ * Named selectors engine. Uses registered XPath selectors to create new expressions.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class PartialNamedSelector extends NamedSelector
+{
+    public function __construct()
+    {
+        $this->registerReplacement('%tagTextMatch%', 'contains(normalize-space(string(.)), %locator%)');
+        $this->registerReplacement('%valueMatch%', 'contains(./@value, %locator%)');
+        $this->registerReplacement('%titleMatch%', 'contains(./@title, %locator%)');
+        $this->registerReplacement('%altMatch%', 'contains(./@alt, %locator%)');
+        $this->registerReplacement('%relMatch%', 'contains(./@rel, %locator%)');
+        $this->registerReplacement('%labelAttributeMatch%', 'contains(./@label, %locator%)');
+
+        parent::__construct();
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorInterface.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorInterface.php
new file mode 100644
index 0000000..e4f5f17
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector;
+
+/**
+ * Mink selector engine interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface SelectorInterface
+{
+    /**
+     * Translates provided locator into XPath.
+     *
+     * @param string|array $locator current selector locator
+     *
+     * @return string
+     */
+    public function translateToXPath($locator);
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorsHandler.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorsHandler.php
new file mode 100644
index 0000000..a19de67
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorsHandler.php
@@ -0,0 +1,125 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector;
+
+use Behat\Mink\Selector\Xpath\Escaper;
+
+/**
+ * Selectors handler.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class SelectorsHandler
+{
+    private $selectors;
+    private $escaper;
+
+    /**
+     * Initializes selectors handler.
+     *
+     * @param SelectorInterface[] $selectors default selectors to register
+     */
+    public function __construct(array $selectors = array())
+    {
+        $this->escaper = new Escaper();
+
+        $this->registerSelector('named_partial', new PartialNamedSelector());
+        $this->registerSelector('named_exact', new ExactNamedSelector());
+        $this->registerSelector('css', new CssSelector());
+
+        foreach ($selectors as $name => $selector) {
+            $this->registerSelector($name, $selector);
+        }
+    }
+
+    /**
+     * Registers new selector engine with specified name.
+     *
+     * @param string            $name     selector engine name
+     * @param SelectorInterface $selector selector engine instance
+     */
+    public function registerSelector($name, SelectorInterface $selector)
+    {
+        $this->selectors[$name] = $selector;
+    }
+
+    /**
+     * Checks whether selector with specified name is registered on handler.
+     *
+     * @param string $name selector engine name
+     *
+     * @return Boolean
+     */
+    public function isSelectorRegistered($name)
+    {
+        return isset($this->selectors[$name]);
+    }
+
+    /**
+     * Returns selector engine with specified name.
+     *
+     * @param string $name selector engine name
+     *
+     * @return SelectorInterface
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function getSelector($name)
+    {
+        if ('named' === $name) {
+            trigger_error(
+                'Using the "named" selector directly from the handler is deprecated as of 1.6 and will be removed in 2.0.'
+                .' Use the "named_partial" or use the "named" selector through the Element API instead.',
+                E_USER_DEPRECATED
+            );
+            $name = 'named_partial';
+        }
+
+        if (!$this->isSelectorRegistered($name)) {
+            throw new \InvalidArgumentException("Selector \"$name\" is not registered.");
+        }
+
+        return $this->selectors[$name];
+    }
+
+    /**
+     * Translates selector with specified name to XPath.
+     *
+     * @param string       $selector selector engine name (registered)
+     * @param string|array $locator  selector locator (an array or a string depending of the selector being used)
+     *
+     * @return string
+     */
+    public function selectorToXpath($selector, $locator)
+    {
+        if ('xpath' === $selector) {
+            if (!is_string($locator)) {
+                throw new \InvalidArgumentException('The xpath selector expects to get a string as locator');
+            }
+
+            return $locator;
+        }
+
+        return $this->getSelector($selector)->translateToXPath($locator);
+    }
+
+    /**
+     * Translates string to XPath literal.
+     *
+     * @param string $s
+     *
+     * @return string
+     */
+    public function xpathLiteral($s)
+    {
+        return $this->escaper->escapeLiteral($s);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Escaper.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Escaper.php
new file mode 100644
index 0000000..be35000
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Escaper.php
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector\Xpath;
+
+/**
+ * XPath escaper.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Escaper
+{
+    /**
+     * Escapes the string as a XPath literal.
+     *
+     * @param string $s
+     *
+     * @return string
+     */
+    public function escapeLiteral($s)
+    {
+        if (false === strpos($s, "'")) {
+            return sprintf("'%s'", $s);
+        }
+
+        if (false === strpos($s, '"')) {
+            return sprintf('"%s"', $s);
+        }
+
+        $string = $s;
+        $parts = array();
+        while (true) {
+            if (false !== $pos = strpos($string, "'")) {
+                $parts[] = sprintf("'%s'", substr($string, 0, $pos));
+                $parts[] = "\"'\"";
+                $string = substr($string, $pos + 1);
+            } else {
+                $parts[] = "'$string'";
+                break;
+            }
+        }
+
+        return sprintf("concat(%s)", implode($parts, ','));
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Manipulator.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Manipulator.php
new file mode 100644
index 0000000..617b314
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Manipulator.php
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink\Selector\Xpath;
+
+/**
+ * XPath manipulation utility.
+ *
+ * @author Graham Bates
+ * @author Christophe Coevoet <stof@notk.org>
+ */
+class Manipulator
+{
+    /**
+     * Regex to find union operators not inside brackets.
+     */
+    const UNION_PATTERN = '/\|(?![^\[]*\])/';
+
+    /**
+     * Prepends the XPath prefix to the given XPath.
+     *
+     * The returned XPath will match elements matching the XPath inside an element
+     * matching the prefix.
+     *
+     * @param string $xpath
+     * @param string $prefix
+     *
+     * @return string
+     */
+    public function prepend($xpath, $prefix)
+    {
+        $expressions = array();
+
+        // If the xpath prefix contains a union we need to wrap it in parentheses.
+        if (preg_match(self::UNION_PATTERN, $prefix)) {
+            $prefix = '(' . $prefix . ')';
+        }
+
+        // Split any unions into individual expressions.
+        foreach (preg_split(self::UNION_PATTERN, $xpath) as $expression) {
+            $expression = trim($expression);
+            $parenthesis = '';
+
+            // If the union is inside some braces, we need to preserve the opening braces and apply
+            // the prefix only inside it.
+            if (preg_match('/^[\(\s*]+/', $expression, $matches)) {
+                $parenthesis = $matches[0];
+                $expression = substr($expression, strlen($parenthesis));
+            }
+
+            // add prefix before element selector
+            if (0 === strpos($expression, '/')) {
+                $expression = $prefix . $expression;
+            } else {
+                $expression = $prefix . '/' . $expression;
+            }
+            $expressions[] = $parenthesis . $expression;
+        }
+
+        return implode(' | ', $expressions);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/Session.php b/core/vendor/behat/mink/src/Behat/Mink/Session.php
new file mode 100644
index 0000000..c67f7f9
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/Session.php
@@ -0,0 +1,352 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink;
+
+use Behat\Mink\Driver\DriverInterface;
+use Behat\Mink\Selector\SelectorsHandler;
+use Behat\Mink\Element\DocumentElement;
+
+/**
+ * Mink session.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Session
+{
+    private $driver;
+    private $page;
+    private $selectorsHandler;
+
+    /**
+     * Initializes session.
+     *
+     * @param DriverInterface  $driver
+     * @param SelectorsHandler $selectorsHandler
+     */
+    public function __construct(DriverInterface $driver, SelectorsHandler $selectorsHandler = null)
+    {
+        $driver->setSession($this);
+
+        if (null === $selectorsHandler) {
+            $selectorsHandler = new SelectorsHandler();
+        }
+
+        $this->driver           = $driver;
+        $this->selectorsHandler = $selectorsHandler;
+        $this->page             = new DocumentElement($this);
+    }
+
+    /**
+     * Checks whether session (driver) was started.
+     *
+     * @return Boolean
+     */
+    public function isStarted()
+    {
+        return $this->driver->isStarted();
+    }
+
+    /**
+     * Starts session driver.
+     *
+     * Calling any action before visiting a page is an undefined behavior.
+     * The only supported method calls on a fresh driver are
+     * - visit()
+     * - setRequestHeader()
+     * - setBasicAuth()
+     * - reset()
+     * - stop()
+     */
+    public function start()
+    {
+        $this->driver->start();
+    }
+
+    /**
+     * Stops session driver.
+     */
+    public function stop()
+    {
+        $this->driver->stop();
+    }
+
+    /**
+     * Restart session driver.
+     */
+    public function restart()
+    {
+        $this->driver->stop();
+        $this->driver->start();
+    }
+
+    /**
+     * Reset session driver state.
+     *
+     * Calling any action before visiting a page is an undefined behavior.
+     * The only supported method calls on a fresh driver are
+     * - visit()
+     * - setRequestHeader()
+     * - setBasicAuth()
+     * - reset()
+     * - stop()
+     */
+    public function reset()
+    {
+        $this->driver->reset();
+    }
+
+    /**
+     * Returns session driver.
+     *
+     * @return DriverInterface
+     */
+    public function getDriver()
+    {
+        return $this->driver;
+    }
+
+    /**
+     * Returns page element.
+     *
+     * @return DocumentElement
+     */
+    public function getPage()
+    {
+        return $this->page;
+    }
+
+    /**
+     * Returns selectors handler.
+     *
+     * @return SelectorsHandler
+     */
+    public function getSelectorsHandler()
+    {
+        return $this->selectorsHandler;
+    }
+
+    /**
+     * Visit specified URL.
+     *
+     * @param string $url url of the page
+     */
+    public function visit($url)
+    {
+        $this->driver->visit($url);
+    }
+
+    /**
+     * Sets HTTP Basic authentication parameters
+     *
+     * @param string|Boolean $user     user name or false to disable authentication
+     * @param string         $password password
+     */
+    public function setBasicAuth($user, $password = '')
+    {
+        $this->driver->setBasicAuth($user, $password);
+    }
+
+    /**
+     * Sets specific request header.
+     *
+     * @param string $name
+     * @param string $value
+     */
+    public function setRequestHeader($name, $value)
+    {
+        $this->driver->setRequestHeader($name, $value);
+    }
+
+    /**
+     * Returns all response headers.
+     *
+     * @return array
+     */
+    public function getResponseHeaders()
+    {
+        return $this->driver->getResponseHeaders();
+    }
+
+    /**
+     * Sets cookie.
+     *
+     * @param string $name
+     * @param string $value
+     */
+    public function setCookie($name, $value = null)
+    {
+        $this->driver->setCookie($name, $value);
+    }
+
+    /**
+     * Returns cookie by name.
+     *
+     * @param string $name
+     *
+     * @return string|null
+     */
+    public function getCookie($name)
+    {
+        return $this->driver->getCookie($name);
+    }
+
+    /**
+     * Returns response status code.
+     *
+     * @return integer
+     */
+    public function getStatusCode()
+    {
+        return $this->driver->getStatusCode();
+    }
+
+    /**
+     * Returns current URL address.
+     *
+     * @return string
+     */
+    public function getCurrentUrl()
+    {
+        return $this->driver->getCurrentUrl();
+    }
+
+    /**
+     * Capture a screenshot of the current window.
+     *
+     * @return string screenshot of MIME type image/* depending
+     *                on driver (e.g., image/png, image/jpeg)
+     */
+    public function getScreenshot()
+    {
+        return $this->driver->getScreenshot();
+    }
+
+    /**
+     * Return the names of all open windows
+     *
+     * @return array Array of all open window's names.
+     */
+    public function getWindowNames()
+    {
+        return $this->driver->getWindowNames();
+    }
+
+    /**
+     * Return the name of the currently active window
+     *
+     * @return string The name of the current window.
+     */
+    public function getWindowName()
+    {
+        return $this->driver->getWindowName();
+    }
+
+    /**
+     * Reloads current session page.
+     */
+    public function reload()
+    {
+        $this->driver->reload();
+    }
+
+    /**
+     * Moves backward 1 page in history.
+     */
+    public function back()
+    {
+        $this->driver->back();
+    }
+
+    /**
+     * Moves forward 1 page in history.
+     */
+    public function forward()
+    {
+        $this->driver->forward();
+    }
+
+    /**
+     * Switches to specific browser window.
+     *
+     * @param string $name window name (null for switching back to main window)
+     */
+    public function switchToWindow($name = null)
+    {
+        $this->driver->switchToWindow($name);
+    }
+
+    /**
+     * Switches to specific iFrame.
+     *
+     * @param string $name iframe name (null for switching back)
+     */
+    public function switchToIFrame($name = null)
+    {
+        $this->driver->switchToIFrame($name);
+    }
+
+    /**
+     * Execute JS in browser.
+     *
+     * @param string $script javascript
+     */
+    public function executeScript($script)
+    {
+        $this->driver->executeScript($script);
+    }
+
+    /**
+     * Execute JS in browser and return it's response.
+     *
+     * @param string $script javascript
+     *
+     * @return string
+     */
+    public function evaluateScript($script)
+    {
+        return $this->driver->evaluateScript($script);
+    }
+
+    /**
+     * Waits some time or until JS condition turns true.
+     *
+     * @param integer $time      time in milliseconds
+     * @param string  $condition JS condition
+     *
+     * @return boolean
+     */
+    public function wait($time, $condition = 'false')
+    {
+        return $this->driver->wait($time, $condition);
+    }
+
+    /**
+     * Set the dimensions of the window.
+     *
+     * @param integer $width  set the window width, measured in pixels
+     * @param integer $height set the window height, measured in pixels
+     * @param string  $name   window name (null for the main window)
+     */
+    public function resizeWindow($width, $height, $name = null)
+    {
+        $this->driver->resizeWindow($width, $height, $name);
+    }
+
+    /**
+     * Maximize the window if it is not maximized already
+     *
+     * @param string $name window name (null for the main window)
+     */
+    public function maximizeWindow($name = null)
+    {
+        $this->driver->maximizeWindow($name);
+    }
+}
diff --git a/core/vendor/behat/mink/src/Behat/Mink/WebAssert.php b/core/vendor/behat/mink/src/Behat/Mink/WebAssert.php
new file mode 100644
index 0000000..8cb3659
--- /dev/null
+++ b/core/vendor/behat/mink/src/Behat/Mink/WebAssert.php
@@ -0,0 +1,748 @@
+<?php
+
+/*
+ * This file is part of the Mink package.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Mink;
+
+use Behat\Mink\Element\Element;
+use Behat\Mink\Element\ElementInterface;
+use Behat\Mink\Element\NodeElement;
+use Behat\Mink\Element\TraversableElement;
+use Behat\Mink\Exception\ElementNotFoundException;
+use Behat\Mink\Exception\ExpectationException;
+use Behat\Mink\Exception\ResponseTextException;
+use Behat\Mink\Exception\ElementHtmlException;
+use Behat\Mink\Exception\ElementTextException;
+
+/**
+ * Mink web assertions tool.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class WebAssert
+{
+    protected $session;
+
+    /**
+     * Initializes assertion engine.
+     *
+     * @param Session $session
+     */
+    public function __construct(Session $session)
+    {
+        $this->session = $session;
+    }
+
+    /**
+     * Checks that current session address is equals to provided one.
+     *
+     * @param string $page
+     *
+     * @throws ExpectationException
+     */
+    public function addressEquals($page)
+    {
+        $expected = $this->cleanUrl($page);
+        $actual   = $this->getCurrentUrlPath();
+
+        $this->assert($actual === $expected, sprintf('Current page is "%s", but "%s" expected.', $actual, $expected));
+    }
+
+    /**
+     * Checks that current session address is not equals to provided one.
+     *
+     * @param string $page
+     *
+     * @throws ExpectationException
+     */
+    public function addressNotEquals($page)
+    {
+        $expected = $this->cleanUrl($page);
+        $actual   = $this->getCurrentUrlPath();
+
+        $this->assert($actual !== $expected, sprintf('Current page is "%s", but should not be.', $actual));
+    }
+
+    /**
+     * Checks that current session address matches regex.
+     *
+     * @param string $regex
+     *
+     * @throws ExpectationException
+     */
+    public function addressMatches($regex)
+    {
+        $actual = $this->getCurrentUrlPath();
+        $message = sprintf('Current page "%s" does not match the regex "%s".', $actual, $regex);
+
+        $this->assert((bool) preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that specified cookie exists and its value equals to a given one
+     *
+     * @param string $name  cookie name
+     * @param string $value cookie value
+     *
+     * @throws ExpectationException
+     */
+    public function cookieEquals($name, $value)
+    {
+        $this->cookieExists($name);
+
+        $actualValue = $this->session->getCookie($name);
+        $message = sprintf('Cookie "%s" value is "%s", but should be "%s".', $name, $actualValue, $value);
+
+        $this->assert($actualValue == $value, $message);
+    }
+
+    /**
+     * Checks that specified cookie exists
+     *
+     * @param string $name cookie name
+     *
+     * @throws ExpectationException
+     */
+    public function cookieExists($name)
+    {
+        $message = sprintf('Cookie "%s" is not set, but should be.', $name);
+        $this->assert($this->session->getCookie($name) !== null, $message);
+    }
+
+    /**
+     * Checks that current response code equals to provided one.
+     *
+     * @param integer $code
+     *
+     * @throws ExpectationException
+     */
+    public function statusCodeEquals($code)
+    {
+        $actual = $this->session->getStatusCode();
+        $message = sprintf('Current response status code is %d, but %d expected.', $actual, $code);
+
+        $this->assert(intval($code) === intval($actual), $message);
+    }
+
+    /**
+     * Checks that current response code not equals to provided one.
+     *
+     * @param integer $code
+     *
+     * @throws ExpectationException
+     */
+    public function statusCodeNotEquals($code)
+    {
+        $actual = $this->session->getStatusCode();
+        $message = sprintf('Current response status code is %d, but should not be.', $actual);
+
+        $this->assert(intval($code) !== intval($actual), $message);
+    }
+
+    /**
+     * Checks that current page contains text.
+     *
+     * @param string $text
+     *
+     * @throws ResponseTextException
+     */
+    public function pageTextContains($text)
+    {
+        $actual = $this->session->getPage()->getText();
+        $actual = preg_replace('/\s+/u', ' ', $actual);
+        $regex  = '/'.preg_quote($text, '/').'/ui';
+        $message = sprintf('The text "%s" was not found anywhere in the text of the current page.', $text);
+
+        $this->assertResponseText((bool) preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that current page does not contains text.
+     *
+     * @param string $text
+     *
+     * @throws ResponseTextException
+     */
+    public function pageTextNotContains($text)
+    {
+        $actual = $this->session->getPage()->getText();
+        $actual = preg_replace('/\s+/u', ' ', $actual);
+        $regex  = '/'.preg_quote($text, '/').'/ui';
+        $message = sprintf('The text "%s" appears in the text of this page, but it should not.', $text);
+
+        $this->assertResponseText(!preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that current page text matches regex.
+     *
+     * @param string $regex
+     *
+     * @throws ResponseTextException
+     */
+    public function pageTextMatches($regex)
+    {
+        $actual = $this->session->getPage()->getText();
+        $message = sprintf('The pattern %s was not found anywhere in the text of the current page.', $regex);
+
+        $this->assertResponseText((bool) preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that current page text does not matches regex.
+     *
+     * @param string $regex
+     *
+     * @throws ResponseTextException
+     */
+    public function pageTextNotMatches($regex)
+    {
+        $actual = $this->session->getPage()->getText();
+        $message = sprintf('The pattern %s was found in the text of the current page, but it should not.', $regex);
+
+        $this->assertResponseText(!preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that page HTML (response content) contains text.
+     *
+     * @param string $text
+     *
+     * @throws ExpectationException
+     */
+    public function responseContains($text)
+    {
+        $actual = $this->session->getPage()->getContent();
+        $regex  = '/'.preg_quote($text, '/').'/ui';
+        $message = sprintf('The string "%s" was not found anywhere in the HTML response of the current page.', $text);
+
+        $this->assert((bool) preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that page HTML (response content) does not contains text.
+     *
+     * @param string $text
+     *
+     * @throws ExpectationException
+     */
+    public function responseNotContains($text)
+    {
+        $actual = $this->session->getPage()->getContent();
+        $regex  = '/'.preg_quote($text, '/').'/ui';
+        $message = sprintf('The string "%s" appears in the HTML response of this page, but it should not.', $text);
+
+        $this->assert(!preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that page HTML (response content) matches regex.
+     *
+     * @param string $regex
+     *
+     * @throws ExpectationException
+     */
+    public function responseMatches($regex)
+    {
+        $actual = $this->session->getPage()->getContent();
+        $message = sprintf('The pattern %s was not found anywhere in the HTML response of the page.', $regex);
+
+        $this->assert((bool) preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that page HTML (response content) does not matches regex.
+     *
+     * @param $regex
+     *
+     * @throws ExpectationException
+     */
+    public function responseNotMatches($regex)
+    {
+        $actual = $this->session->getPage()->getContent();
+        $message = sprintf('The pattern %s was found in the HTML response of the page, but it should not.', $regex);
+
+        $this->assert(!preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that there is specified number of specific elements on the page.
+     *
+     * @param string           $selectorType element selector type (css, xpath)
+     * @param string|array     $selector     element selector
+     * @param integer          $count        expected count
+     * @param ElementInterface $container    document to check against
+     *
+     * @throws ExpectationException
+     */
+    public function elementsCount($selectorType, $selector, $count, ElementInterface $container = null)
+    {
+        $container = $container ?: $this->session->getPage();
+        $nodes = $container->findAll($selectorType, $selector);
+
+        $message = sprintf(
+            '%d %s found on the page, but should be %d.',
+            count($nodes),
+            $this->getMatchingElementRepresentation($selectorType, $selector, count($nodes) !== 1),
+            $count
+        );
+
+        $this->assert(intval($count) === count($nodes), $message);
+    }
+
+    /**
+     * Checks that specific element exists on the current page.
+     *
+     * @param string           $selectorType element selector type (css, xpath)
+     * @param string|array     $selector     element selector
+     * @param ElementInterface $container    document to check against
+     *
+     * @return NodeElement
+     *
+     * @throws ElementNotFoundException
+     */
+    public function elementExists($selectorType, $selector, ElementInterface $container = null)
+    {
+        $container = $container ?: $this->session->getPage();
+        $node = $container->find($selectorType, $selector);
+
+        if (null === $node) {
+            if (is_array($selector)) {
+                $selector = implode(' ', $selector);
+            }
+
+            throw new ElementNotFoundException($this->session, 'element', $selectorType, $selector);
+        }
+
+        return $node;
+    }
+
+    /**
+     * Checks that specific element does not exists on the current page.
+     *
+     * @param string           $selectorType element selector type (css, xpath)
+     * @param string|array     $selector     element selector
+     * @param ElementInterface $container    document to check against
+     *
+     * @throws ExpectationException
+     */
+    public function elementNotExists($selectorType, $selector, ElementInterface $container = null)
+    {
+        $container = $container ?: $this->session->getPage();
+        $node = $container->find($selectorType, $selector);
+
+        $message = sprintf(
+            'An %s appears on this page, but it should not.',
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assert(null === $node, $message);
+    }
+
+    /**
+     * Checks that specific element contains text.
+     *
+     * @param string       $selectorType element selector type (css, xpath)
+     * @param string|array $selector     element selector
+     * @param string       $text         expected text
+     *
+     * @throws ElementTextException
+     */
+    public function elementTextContains($selectorType, $selector, $text)
+    {
+        $element = $this->elementExists($selectorType, $selector);
+        $actual  = $element->getText();
+        $regex   = '/'.preg_quote($text, '/').'/ui';
+
+        $message = sprintf(
+            'The text "%s" was not found in the text of the %s.',
+            $text,
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assertElementText((bool) preg_match($regex, $actual), $message, $element);
+    }
+
+    /**
+     * Checks that specific element does not contains text.
+     *
+     * @param string       $selectorType element selector type (css, xpath)
+     * @param string|array $selector     element selector
+     * @param string       $text         expected text
+     *
+     * @throws ElementTextException
+     */
+    public function elementTextNotContains($selectorType, $selector, $text)
+    {
+        $element = $this->elementExists($selectorType, $selector);
+        $actual  = $element->getText();
+        $regex   = '/'.preg_quote($text, '/').'/ui';
+
+        $message = sprintf(
+            'The text "%s" appears in the text of the %s, but it should not.',
+            $text,
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assertElementText(!preg_match($regex, $actual), $message, $element);
+    }
+
+    /**
+     * Checks that specific element contains HTML.
+     *
+     * @param string       $selectorType element selector type (css, xpath)
+     * @param string|array $selector     element selector
+     * @param string       $html         expected text
+     *
+     * @throws ElementHtmlException
+     */
+    public function elementContains($selectorType, $selector, $html)
+    {
+        $element = $this->elementExists($selectorType, $selector);
+        $actual  = $element->getHtml();
+        $regex   = '/'.preg_quote($html, '/').'/ui';
+
+        $message = sprintf(
+            'The string "%s" was not found in the HTML of the %s.',
+            $html,
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assertElement((bool) preg_match($regex, $actual), $message, $element);
+    }
+
+    /**
+     * Checks that specific element does not contains HTML.
+     *
+     * @param string       $selectorType element selector type (css, xpath)
+     * @param string|array $selector     element selector
+     * @param string       $html         expected text
+     *
+     * @throws ElementHtmlException
+     */
+    public function elementNotContains($selectorType, $selector, $html)
+    {
+        $element = $this->elementExists($selectorType, $selector);
+        $actual  = $element->getHtml();
+        $regex   = '/'.preg_quote($html, '/').'/ui';
+
+        $message = sprintf(
+            'The string "%s" appears in the HTML of the %s, but it should not.',
+            $html,
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assertElement(!preg_match($regex, $actual), $message, $element);
+    }
+
+    /**
+     * Checks that an attribute exists in an element.
+     *
+     * @param string       $selectorType
+     * @param string|array $selector
+     * @param string       $attribute
+     *
+     * @return NodeElement
+     *
+     * @throws ElementHtmlException
+     */
+    public function elementAttributeExists($selectorType, $selector, $attribute)
+    {
+        $element = $this->elementExists($selectorType, $selector);
+
+        $message = sprintf(
+            'The attribute "%s" was not found in the %s.',
+            $attribute,
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assertElement($element->hasAttribute($attribute), $message, $element);
+
+        return $element;
+    }
+
+    /**
+     * Checks that an attribute of a specific elements contains text.
+     *
+     * @param string       $selectorType
+     * @param string|array $selector
+     * @param string       $attribute
+     * @param string       $text
+     *
+     * @throws ElementHtmlException
+     */
+    public function elementAttributeContains($selectorType, $selector, $attribute, $text)
+    {
+        $element = $this->elementAttributeExists($selectorType, $selector, $attribute);
+        $actual  = $element->getAttribute($attribute);
+        $regex   = '/'.preg_quote($text, '/').'/ui';
+
+        $message = sprintf(
+            'The text "%s" was not found in the attribute "%s" of the %s.',
+            $text,
+            $attribute,
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assertElement((bool) preg_match($regex, $actual), $message, $element);
+    }
+
+    /**
+     * Checks that an attribute of a specific elements does not contain text.
+     *
+     * @param string       $selectorType
+     * @param string|array $selector
+     * @param string       $attribute
+     * @param string       $text
+     *
+     * @throws ElementHtmlException
+     */
+    public function elementAttributeNotContains($selectorType, $selector, $attribute, $text)
+    {
+        $element = $this->elementAttributeExists($selectorType, $selector, $attribute);
+        $actual  = $element->getAttribute($attribute);
+        $regex   = '/'.preg_quote($text, '/').'/ui';
+
+        $message = sprintf(
+            'The text "%s" was found in the attribute "%s" of the %s.',
+            $text,
+            $attribute,
+            $this->getMatchingElementRepresentation($selectorType, $selector)
+        );
+
+        $this->assertElement(!preg_match($regex, $actual), $message, $element);
+    }
+
+    /**
+     * Checks that specific field exists on the current page.
+     *
+     * @param string             $field     field id|name|label|value
+     * @param TraversableElement $container document to check against
+     *
+     * @return NodeElement
+     *
+     * @throws ElementNotFoundException
+     */
+    public function fieldExists($field, TraversableElement $container = null)
+    {
+        $container = $container ?: $this->session->getPage();
+        $node = $container->findField($field);
+
+        if (null === $node) {
+            throw new ElementNotFoundException($this->session, 'form field', 'id|name|label|value', $field);
+        }
+
+        return $node;
+    }
+
+    /**
+     * Checks that specific field does not exists on the current page.
+     *
+     * @param string             $field     field id|name|label|value
+     * @param TraversableElement $container document to check against
+     *
+     * @throws ExpectationException
+     */
+    public function fieldNotExists($field, TraversableElement $container = null)
+    {
+        $container = $container ?: $this->session->getPage();
+        $node = $container->findField($field);
+
+        $this->assert(null === $node, sprintf('A field "%s" appears on this page, but it should not.', $field));
+    }
+
+    /**
+     * Checks that specific field have provided value.
+     *
+     * @param string             $field     field id|name|label|value
+     * @param string             $value     field value
+     * @param TraversableElement $container document to check against
+     *
+     * @throws ExpectationException
+     */
+    public function fieldValueEquals($field, $value, TraversableElement $container = null)
+    {
+        $node   = $this->fieldExists($field, $container);
+        $actual = $node->getValue();
+        $regex  = '/^'.preg_quote($value, '/').'$/ui';
+
+        $message = sprintf('The field "%s" value is "%s", but "%s" expected.', $field, $actual, $value);
+
+        $this->assert((bool) preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that specific field have provided value.
+     *
+     * @param string             $field     field id|name|label|value
+     * @param string             $value     field value
+     * @param TraversableElement $container document to check against
+     *
+     * @throws ExpectationException
+     */
+    public function fieldValueNotEquals($field, $value, TraversableElement $container = null)
+    {
+        $node   = $this->fieldExists($field, $container);
+        $actual = $node->getValue();
+        $regex  = '/^'.preg_quote($value, '/').'$/ui';
+
+        $message = sprintf('The field "%s" value is "%s", but it should not be.', $field, $actual);
+
+        $this->assert(!preg_match($regex, $actual), $message);
+    }
+
+    /**
+     * Checks that specific checkbox is checked.
+     *
+     * @param string             $field     field id|name|label|value
+     * @param TraversableElement $container document to check against
+     *
+     * @throws ExpectationException
+     */
+    public function checkboxChecked($field, TraversableElement $container = null)
+    {
+        $node = $this->fieldExists($field, $container);
+
+        $this->assert($node->isChecked(), sprintf('Checkbox "%s" is not checked, but it should be.', $field));
+    }
+
+    /**
+     * Checks that specific checkbox is unchecked.
+     *
+     * @param string             $field     field id|name|label|value
+     * @param TraversableElement $container document to check against
+     *
+     * @throws ExpectationException
+     */
+    public function checkboxNotChecked($field, TraversableElement $container = null)
+    {
+        $node = $this->fieldExists($field, $container);
+
+        $this->assert(!$node->isChecked(), sprintf('Checkbox "%s" is checked, but it should not be.', $field));
+    }
+
+    /**
+     * Gets current url of the page.
+     *
+     * @return string
+     */
+    protected function getCurrentUrlPath()
+    {
+        return $this->cleanUrl($this->session->getCurrentUrl());
+    }
+
+    /**
+     * Trims scriptname from the URL.
+     *
+     * @param string $url
+     *
+     * @return string
+     */
+    protected function cleanUrl($url)
+    {
+        $parts = parse_url($url);
+        $fragment = empty($parts['fragment']) ? '' : '#' . $parts['fragment'];
+
+        return preg_replace('/^\/[^\.\/]+\.php/', '', $parts['path']) . $fragment;
+    }
+
+    /**
+     * Asserts a condition.
+     *
+     * @param bool   $condition
+     * @param string $message   Failure message
+     *
+     * @throws ExpectationException when the condition is not fulfilled
+     */
+    private function assert($condition, $message)
+    {
+        if ($condition) {
+            return;
+        }
+
+        throw new ExpectationException($message, $this->session);
+    }
+
+    /**
+     * Asserts a condition involving the response text.
+     *
+     * @param bool   $condition
+     * @param string $message   Failure message
+     *
+     * @throws ResponseTextException when the condition is not fulfilled
+     */
+    private function assertResponseText($condition, $message)
+    {
+        if ($condition) {
+            return;
+        }
+
+        throw new ResponseTextException($message, $this->session);
+    }
+
+    /**
+     * Asserts a condition on an element.
+     *
+     * @param bool    $condition
+     * @param string  $message   Failure message
+     * @param Element $element
+     *
+     * @throws ElementHtmlException when the condition is not fulfilled
+     */
+    private function assertElement($condition, $message, Element $element)
+    {
+        if ($condition) {
+            return;
+        }
+
+        throw new ElementHtmlException($message, $this->session, $element);
+    }
+
+    /**
+     * Asserts a condition involving the text of an element.
+     *
+     * @param bool    $condition
+     * @param string  $message   Failure message
+     * @param Element $element
+     *
+     * @throws ElementTextException when the condition is not fulfilled
+     */
+    private function assertElementText($condition, $message, Element $element)
+    {
+        if ($condition) {
+            return;
+        }
+
+        throw new ElementTextException($message, $this->session, $element);
+    }
+
+    /**
+     * @param string       $selectorType
+     * @param string|array $selector
+     * @param boolean      $plural
+     *
+     * @return string
+     */
+    private function getMatchingElementRepresentation($selectorType, $selector, $plural = false)
+    {
+        $pluralization = $plural ? 's' : '';
+
+        if (in_array($selectorType, array('named', 'named_exact', 'named_partial'))
+            && is_array($selector) && 2 === count($selector)
+        ) {
+            return sprintf('%s%s matching locator "%s"', $selector[0], $pluralization, $selector[1]);
+        }
+
+        if (is_array($selector)) {
+            $selector = implode(' ', $selector);
+        }
+
+        return sprintf('element%s matching %s "%s"', $pluralization, $selectorType, $selector);
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Driver/CoreDriverTest.php b/core/vendor/behat/mink/tests/Driver/CoreDriverTest.php
new file mode 100644
index 0000000..d99b17f
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Driver/CoreDriverTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Behat\Mink\Tests\Driver;
+
+class CoreDriverTest extends \PHPUnit_Framework_TestCase
+{
+    public function testNoExtraMethods()
+    {
+        $interfaceRef = new \ReflectionClass('Behat\Mink\Driver\DriverInterface');
+        $coreDriverRef = new \ReflectionClass('Behat\Mink\Driver\CoreDriver');
+
+        foreach ($coreDriverRef->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
+            $this->assertTrue(
+                $interfaceRef->hasMethod($method->getName()),
+                sprintf('CoreDriver should not implement methods which are not part of the DriverInterface but %s found', $method->getName())
+            );
+        }
+    }
+
+    /**
+     * @dataProvider getDriverInterfaceMethods
+     */
+    public function testInterfaceMethods(\ReflectionMethod $method)
+    {
+        $refl = new \ReflectionClass('Behat\Mink\Driver\CoreDriver');
+
+        $this->assertFalse(
+            $refl->getMethod($method->getName())->isAbstract(),
+            sprintf('CoreDriver should implement a dummy %s method', $method->getName())
+        );
+
+        $driver = $this->getMockForAbstractClass('Behat\Mink\Driver\CoreDriver');
+
+        $this->setExpectedException('Behat\Mink\Exception\UnsupportedDriverActionException');
+        call_user_func_array(array($driver, $method->getName()), $this->getArguments($method));
+    }
+
+    public function getDriverInterfaceMethods()
+    {
+        $ref = new \ReflectionClass('Behat\Mink\Driver\DriverInterface');
+
+        return array_map(function ($method) {
+            return array($method);
+        }, $ref->getMethods());
+    }
+
+    private function getArguments(\ReflectionMethod $method)
+    {
+        $arguments = array();
+
+        foreach ($method->getParameters() as $parameter) {
+            $arguments[] = $this->getArgument($parameter);
+        }
+
+        return $arguments;
+    }
+
+    private function getArgument(\ReflectionParameter $argument)
+    {
+        if ($argument->isOptional()) {
+            return $argument->getDefaultValue();
+        }
+
+        if ($argument->allowsNull()) {
+            return null;
+        }
+
+        if ($argument->getClass()) {
+            return $this->getMockBuilder($argument->getClass()->getName())
+                ->disableOriginalConstructor()
+                ->getMock();
+        }
+
+        return null;
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Element/DocumentElementTest.php b/core/vendor/behat/mink/tests/Element/DocumentElementTest.php
new file mode 100644
index 0000000..bb42b68
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Element/DocumentElementTest.php
@@ -0,0 +1,446 @@
+<?php
+
+namespace Behat\Mink\Tests\Element;
+
+use Behat\Mink\Element\DocumentElement;
+
+class DocumentElementTest extends ElementTest
+{
+    /**
+     * Page.
+     *
+     * @var DocumentElement
+     */
+    private $document;
+
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->document = new DocumentElement($this->session);
+    }
+
+    public function testGetSession()
+    {
+        $this->assertEquals($this->session, $this->document->getSession());
+    }
+
+    public function testFindAll()
+    {
+        $xpath = 'h3[a]';
+        $css = 'h3 > a';
+
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->will($this->returnValueMap(array(
+                array('//html/' . $xpath, array(2, 3, 4)),
+                array('//html/' . $css, array(1, 2)),
+            )));
+
+        $this->selectors
+            ->expects($this->exactly(2))
+            ->method('selectorToXpath')
+            ->will($this->returnValueMap(array(
+                array('xpath', $xpath, $xpath),
+                array('css', $css, $css),
+            )));
+
+        $this->assertEquals(3, count($this->document->findAll('xpath', $xpath)));
+        $this->assertEquals(2, count($this->document->findAll('css', $css)));
+    }
+
+    public function testFind()
+    {
+        $this->driver
+            ->expects($this->exactly(3))
+            ->method('find')
+            ->with('//html/h3[a]')
+            ->will($this->onConsecutiveCalls(array(2, 3, 4), array(1, 2), array()));
+
+        $xpath = 'h3[a]';
+        $css = 'h3 > a';
+
+        $this->selectors
+            ->expects($this->exactly(3))
+            ->method('selectorToXpath')
+            ->will($this->returnValueMap(array(
+                array('xpath', $xpath, $xpath),
+                array('xpath', $xpath, $xpath),
+                array('css', $css, $xpath),
+            )));
+
+        $this->assertEquals(2, $this->document->find('xpath', $xpath));
+        $this->assertEquals(1, $this->document->find('css', $css));
+        $this->assertNull($this->document->find('xpath', $xpath));
+    }
+
+    public function testFindField()
+    {
+        $this->mockNamedFinder(
+            '//field',
+            array('field1', 'field2', 'field3'),
+            array('field', 'some field')
+        );
+
+        $this->assertEquals('field1', $this->document->findField('some field'));
+        $this->assertEquals(null, $this->document->findField('some field'));
+    }
+
+    public function testFindLink()
+    {
+        $this->mockNamedFinder(
+            '//link',
+            array('link1', 'link2', 'link3'),
+            array('link', 'some link')
+        );
+
+        $this->assertEquals('link1', $this->document->findLink('some link'));
+        $this->assertEquals(null, $this->document->findLink('some link'));
+    }
+
+    public function testFindButton()
+    {
+        $this->mockNamedFinder(
+            '//button',
+            array('button1', 'button2', 'button3'),
+            array('button', 'some button')
+        );
+
+        $this->assertEquals('button1', $this->document->findButton('some button'));
+        $this->assertEquals(null, $this->document->findButton('some button'));
+    }
+
+    public function testFindById()
+    {
+        $xpath = '//*[@id=some-item-2]';
+
+        $this->mockNamedFinder($xpath, array(array('id2', 'id3'), array()), array('id', 'some-item-2'));
+
+        $this->assertEquals('id2', $this->document->findById('some-item-2'));
+        $this->assertEquals(null, $this->document->findById('some-item-2'));
+    }
+
+    public function testHasSelector()
+    {
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('//html/some xpath')
+            ->will($this->onConsecutiveCalls(array('id2', 'id3'), array()));
+
+        $this->selectors
+            ->expects($this->exactly(2))
+            ->method('selectorToXpath')
+            ->with('xpath', 'some xpath')
+            ->will($this->returnValue('some xpath'));
+
+        $this->assertTrue($this->document->has('xpath', 'some xpath'));
+        $this->assertFalse($this->document->has('xpath', 'some xpath'));
+    }
+
+    public function testHasContent()
+    {
+        $this->mockNamedFinder(
+            '//some content',
+            array('item1', 'item2'),
+            array('content', 'some content')
+        );
+
+        $this->assertTrue($this->document->hasContent('some content'));
+        $this->assertFalse($this->document->hasContent('some content'));
+    }
+
+    public function testHasLink()
+    {
+        $this->mockNamedFinder(
+            '//link',
+            array('link1', 'link2', 'link3'),
+            array('link', 'some link')
+        );
+
+        $this->assertTrue($this->document->hasLink('some link'));
+        $this->assertFalse($this->document->hasLink('some link'));
+    }
+
+    public function testHasButton()
+    {
+        $this->mockNamedFinder(
+            '//button',
+            array('button1', 'button2', 'button3'),
+            array('button', 'some button')
+        );
+
+        $this->assertTrue($this->document->hasButton('some button'));
+        $this->assertFalse($this->document->hasButton('some button'));
+    }
+
+    public function testHasField()
+    {
+        $this->mockNamedFinder(
+            '//field',
+            array('field1', 'field2', 'field3'),
+            array('field', 'some field')
+        );
+
+        $this->assertTrue($this->document->hasField('some field'));
+        $this->assertFalse($this->document->hasField('some field'));
+    }
+
+    public function testHasCheckedField()
+    {
+        $checkbox = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $checkbox
+            ->expects($this->exactly(2))
+            ->method('isChecked')
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->mockNamedFinder(
+            '//field',
+            array(array($checkbox), array(), array($checkbox)),
+            array('field', 'some checkbox'),
+            3
+        );
+
+        $this->assertTrue($this->document->hasCheckedField('some checkbox'));
+        $this->assertFalse($this->document->hasCheckedField('some checkbox'));
+        $this->assertFalse($this->document->hasCheckedField('some checkbox'));
+    }
+
+    public function testHasUncheckedField()
+    {
+        $checkbox = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $checkbox
+            ->expects($this->exactly(2))
+            ->method('isChecked')
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->mockNamedFinder(
+            '//field',
+            array(array($checkbox), array(), array($checkbox)),
+            array('field', 'some checkbox'),
+            3
+        );
+
+        $this->assertFalse($this->document->hasUncheckedField('some checkbox'));
+        $this->assertFalse($this->document->hasUncheckedField('some checkbox'));
+        $this->assertTrue($this->document->hasUncheckedField('some checkbox'));
+    }
+
+    public function testHasSelect()
+    {
+        $this->mockNamedFinder(
+            '//select',
+            array('select'),
+            array('select', 'some select field')
+        );
+
+        $this->assertTrue($this->document->hasSelect('some select field'));
+        $this->assertFalse($this->document->hasSelect('some select field'));
+    }
+
+    public function testHasTable()
+    {
+        $this->mockNamedFinder(
+            '//table',
+            array('table'),
+            array('table', 'some table')
+        );
+
+        $this->assertTrue($this->document->hasTable('some table'));
+        $this->assertFalse($this->document->hasTable('some table'));
+    }
+
+    public function testClickLink()
+    {
+        $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $node
+            ->expects($this->once())
+            ->method('click');
+
+        $this->mockNamedFinder(
+            '//link',
+            array($node),
+            array('link', 'some link')
+        );
+
+        $this->document->clickLink('some link');
+        $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException');
+        $this->document->clickLink('some link');
+    }
+
+    public function testClickButton()
+    {
+        $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $node
+            ->expects($this->once())
+            ->method('press');
+
+        $this->mockNamedFinder(
+            '//button',
+            array($node),
+            array('button', 'some button')
+        );
+
+        $this->document->pressButton('some button');
+        $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException');
+        $this->document->pressButton('some button');
+    }
+
+    public function testFillField()
+    {
+        $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $node
+            ->expects($this->once())
+            ->method('setValue')
+            ->with('some val');
+
+        $this->mockNamedFinder(
+            '//field',
+            array($node),
+            array('field', 'some field')
+        );
+
+        $this->document->fillField('some field', 'some val');
+        $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException');
+        $this->document->fillField('some field', 'some val');
+    }
+
+    public function testCheckField()
+    {
+        $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $node
+            ->expects($this->once())
+            ->method('check');
+
+        $this->mockNamedFinder(
+            '//field',
+            array($node),
+            array('field', 'some field')
+        );
+
+        $this->document->checkField('some field');
+        $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException');
+        $this->document->checkField('some field');
+    }
+
+    public function testUncheckField()
+    {
+        $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $node
+            ->expects($this->once())
+            ->method('uncheck');
+
+        $this->mockNamedFinder(
+            '//field',
+            array($node),
+            array('field', 'some field')
+        );
+
+        $this->document->uncheckField('some field');
+        $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException');
+        $this->document->uncheckField('some field');
+    }
+
+    public function testSelectField()
+    {
+        $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $node
+            ->expects($this->once())
+            ->method('selectOption')
+            ->with('option2');
+
+        $this->mockNamedFinder(
+            '//field',
+            array($node),
+            array('field', 'some field')
+        );
+
+        $this->document->selectFieldOption('some field', 'option2');
+        $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException');
+        $this->document->selectFieldOption('some field', 'option2');
+    }
+
+    public function testAttachFileToField()
+    {
+        $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $node
+            ->expects($this->once())
+            ->method('attachFile')
+            ->with('/path/to/file');
+
+        $this->mockNamedFinder(
+            '//field',
+            array($node),
+            array('field', 'some field')
+        );
+
+        $this->document->attachFileToField('some field', '/path/to/file');
+        $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException');
+        $this->document->attachFileToField('some field', '/path/to/file');
+    }
+
+    public function testGetContent()
+    {
+        $expects = 'page content';
+        $this->driver
+            ->expects($this->once())
+            ->method('getContent')
+            ->will($this->returnValue($expects));
+
+        $this->assertEquals($expects, $this->document->getContent());
+    }
+
+    public function testGetText()
+    {
+        $expects = 'val1';
+        $this->driver
+            ->expects($this->once())
+            ->method('getText')
+            ->with('//html')
+            ->will($this->returnValue($expects));
+
+        $this->assertEquals($expects, $this->document->getText());
+    }
+
+    public function testGetHtml()
+    {
+        $expects = 'val1';
+        $this->driver
+            ->expects($this->once())
+            ->method('getHtml')
+            ->with('//html')
+            ->will($this->returnValue($expects));
+
+        $this->assertEquals($expects, $this->document->getHtml());
+    }
+
+    public function testGetOuterHtml()
+    {
+        $expects = 'val1';
+        $this->driver
+            ->expects($this->once())
+            ->method('getOuterHtml')
+            ->with('//html')
+            ->will($this->returnValue($expects));
+
+        $this->assertEquals($expects, $this->document->getOuterHtml());
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Element/ElementTest.php b/core/vendor/behat/mink/tests/Element/ElementTest.php
new file mode 100644
index 0000000..1f9240a
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Element/ElementTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Behat\Mink\Tests\Element;
+
+use Behat\Mink\Driver\DriverInterface;
+use Behat\Mink\Session;
+use Behat\Mink\Selector\SelectorsHandler;
+
+abstract class ElementTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Session.
+     *
+     * @var Session
+     */
+    protected $session;
+
+    /**
+     * @var DriverInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $driver;
+
+    /**
+     * Selectors.
+     *
+     * @var SelectorsHandler|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $selectors;
+
+    protected function setUp()
+    {
+        $this->driver = $this->getMockBuilder('Behat\Mink\Driver\DriverInterface')->getMock();
+        $this->driver
+            ->expects($this->once())
+            ->method('setSession');
+
+        $this->selectors = $this->getMockBuilder('Behat\Mink\Selector\SelectorsHandler')->getMock();
+        $this->session = new Session($this->driver, $this->selectors);
+
+        $this->selectors
+            ->expects($this->any())
+            ->method('xpathLiteral')
+            ->will($this->returnArgument(0));
+    }
+
+    protected function mockNamedFinder($xpath, array $results, $locator, $times = 2)
+    {
+        if (!is_array($results[0])) {
+            $results = array($results, array());
+        }
+
+        // In case of empty results, a second call will be done using the partial selector
+        $processedResults = array();
+        foreach ($results as $result) {
+            $processedResults[] = $result;
+            if (empty($result)) {
+                $processedResults[] = $result;
+                $times++;
+            }
+        }
+
+        $returnValue = call_user_func_array(array($this, 'onConsecutiveCalls'), $processedResults);
+
+        $this->driver
+            ->expects($this->exactly($times))
+            ->method('find')
+            ->with('//html' . $xpath)
+            ->will($returnValue);
+
+        $this->selectors
+            ->expects($this->exactly($times))
+            ->method('selectorToXpath')
+            ->with($this->logicalOr('named_exact', 'named_partial'), $locator)
+            ->will($this->returnValue($xpath));
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Element/NodeElementTest.php b/core/vendor/behat/mink/tests/Element/NodeElementTest.php
new file mode 100644
index 0000000..b6a8bde
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Element/NodeElementTest.php
@@ -0,0 +1,593 @@
+<?php
+
+namespace Behat\Mink\Tests\Element;
+
+use Behat\Mink\Element\NodeElement;
+
+class NodeElementTest extends ElementTest
+{
+    public function testGetXpath()
+    {
+        $node = new NodeElement('some custom xpath', $this->session);
+
+        $this->assertEquals('some custom xpath', $node->getXpath());
+        $this->assertNotEquals('not some custom xpath', $node->getXpath());
+    }
+
+    public function testGetText()
+    {
+        $expected = 'val1';
+        $node = new NodeElement('text_tag', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getText')
+            ->with('text_tag')
+            ->will($this->returnValue($expected));
+
+        $this->assertEquals($expected, $node->getText());
+    }
+
+    public function testGetOuterHtml()
+    {
+        $expected = 'val1';
+        $node = new NodeElement('text_tag', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getOuterHtml')
+            ->with('text_tag')
+            ->will($this->returnValue($expected));
+
+        $this->assertEquals($expected, $node->getOuterHtml());
+    }
+
+    public function testElementIsValid()
+    {
+        $elementXpath = 'some xpath';
+        $node = new NodeElement($elementXpath, $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('find')
+            ->with($elementXpath)
+            ->will($this->returnValue(array($elementXpath)));
+
+        $this->assertTrue($node->isValid());
+    }
+
+    public function testElementIsNotValid()
+    {
+        $node = new NodeElement('some xpath', $this->session);
+
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('some xpath')
+            ->will($this->onConsecutiveCalls(array(), array('xpath1', 'xpath2')));
+
+        $this->assertFalse($node->isValid(), 'no elements found is invalid element');
+        $this->assertFalse($node->isValid(), 'more then 1 element found is invalid element');
+    }
+
+    public function testWaitForSuccess()
+    {
+        $callCounter = 0;
+        $node = new NodeElement('some xpath', $this->session);
+
+        $result = $node->waitFor(5, function ($givenNode) use (&$callCounter) {
+            $callCounter++;
+
+            if (1 === $callCounter) {
+                return null;
+            } elseif (2 === $callCounter) {
+                return false;
+            } elseif (3 === $callCounter) {
+                return array();
+            }
+
+            return $givenNode;
+        });
+
+        $this->assertEquals(4, $callCounter, '->waitFor() tries to locate element several times before failing');
+        $this->assertSame($node, $result, '->waitFor() returns node found in callback');
+    }
+
+    public function testWaitForTimeout()
+    {
+        $node = new NodeElement('some xpath', $this->session);
+
+        $expectedTimeout = 2;
+        $startTime = microtime(true);
+        $result = $node->waitFor($expectedTimeout, function () {
+            return null;
+        });
+        $endTime = microtime(true);
+
+        $this->assertNull($result, '->waitFor() returns whatever callback gives');
+        $this->assertEquals($expectedTimeout, round($endTime - $startTime));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testWaitForFailure()
+    {
+        $node = new NodeElement('some xpath', $this->session);
+        $node->waitFor(5, 'not a callable');
+    }
+
+    public function testHasAttribute()
+    {
+        $node = new NodeElement('input_tag', $this->session);
+
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('getAttribute')
+            ->with('input_tag', 'href')
+            ->will($this->onConsecutiveCalls(null, 'http://...'));
+
+        $this->assertFalse($node->hasAttribute('href'));
+        $this->assertTrue($node->hasAttribute('href'));
+    }
+
+    public function testGetAttribute()
+    {
+        $expected = 'http://...';
+        $node = new NodeElement('input_tag', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getAttribute')
+            ->with('input_tag', 'href')
+            ->will($this->returnValue($expected));
+
+        $this->assertEquals($expected, $node->getAttribute('href'));
+    }
+
+    public function testHasClass()
+    {
+        $node = new NodeElement('input_tag', $this->session);
+
+        $this->driver
+            ->expects($this->exactly(6))
+            ->method('getAttribute')
+            ->with('input_tag', 'class')
+            ->will($this->returnValue('class1 class2'));
+
+        $this->assertTrue($node->hasClass('class1'));
+        $this->assertTrue($node->hasClass('class2'));
+        $this->assertFalse($node->hasClass('class3'));
+    }
+
+    public function testHasClassWithoutArgument()
+    {
+        $node = new NodeElement('input_tag', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getAttribute')
+            ->with('input_tag', 'class')
+            ->will($this->returnValue(null));
+
+        $this->assertFalse($node->hasClass('class3'));
+    }
+
+    public function testGetValue()
+    {
+        $expected = 'val1';
+        $node = new NodeElement('input_tag', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getValue')
+            ->with('input_tag')
+            ->will($this->returnValue($expected));
+
+        $this->assertEquals($expected, $node->getValue());
+    }
+
+    public function testSetValue()
+    {
+        $expected = 'new_val';
+        $node = new NodeElement('input_tag', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('setValue')
+            ->with('input_tag', $expected);
+
+        $node->setValue($expected);
+    }
+
+    public function testClick()
+    {
+        $node = new NodeElement('link_or_button', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('click')
+            ->with('link_or_button');
+
+        $node->click();
+    }
+
+    public function testPress()
+    {
+        $node = new NodeElement('link_or_button', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('click')
+            ->with('link_or_button');
+
+        $node->press();
+    }
+
+    public function testRightClick()
+    {
+        $node = new NodeElement('elem', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('rightClick')
+            ->with('elem');
+
+        $node->rightClick();
+    }
+
+    public function testDoubleClick()
+    {
+        $node = new NodeElement('elem', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('doubleClick')
+            ->with('elem');
+
+        $node->doubleClick();
+    }
+
+    public function testCheck()
+    {
+        $node = new NodeElement('checkbox_or_radio', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('check')
+            ->with('checkbox_or_radio');
+
+        $node->check();
+    }
+
+    public function testUncheck()
+    {
+        $node = new NodeElement('checkbox_or_radio', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('uncheck')
+            ->with('checkbox_or_radio');
+
+        $node->uncheck();
+    }
+
+    public function testSelectOption()
+    {
+        $node = new NodeElement('select', $this->session);
+        $option = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $option
+            ->expects($this->once())
+            ->method('getValue')
+            ->will($this->returnValue('item1'));
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getTagName')
+            ->with('select')
+            ->will($this->returnValue('select'));
+
+        $this->driver
+            ->expects($this->once())
+            ->method('find')
+            ->with('select/option')
+            ->will($this->returnValue(array($option)));
+
+        $this->selectors
+            ->expects($this->once())
+            ->method('selectorToXpath')
+            ->with('named_exact', array('option', 'item1'))
+            ->will($this->returnValue('option'));
+
+        $this->driver
+            ->expects($this->once())
+            ->method('selectOption')
+            ->with('select', 'item1', false);
+
+        $node->selectOption('item1');
+    }
+
+    /**
+     * @expectedException \Behat\Mink\Exception\ElementNotFoundException
+     */
+    public function testSelectOptionNotFound()
+    {
+        $node = new NodeElement('select', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getTagName')
+            ->with('select')
+            ->will($this->returnValue('select'));
+
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('select/option')
+            ->will($this->returnValue(array()));
+
+        $this->selectors
+            ->expects($this->exactly(2))
+            ->method('selectorToXpath')
+            ->with($this->logicalOr('named_exact', 'named_partial'), array('option', 'item1'))
+            ->will($this->returnValue('option'));
+
+        $node->selectOption('item1');
+    }
+
+    public function testSelectOptionOtherTag()
+    {
+        $node = new NodeElement('div', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getTagName')
+            ->with('div')
+            ->will($this->returnValue('div'));
+
+        $this->driver
+            ->expects($this->once())
+            ->method('selectOption')
+            ->with('div', 'item1', false);
+
+        $node->selectOption('item1');
+    }
+
+    public function testGetTagName()
+    {
+        $node = new NodeElement('html//h3', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('getTagName')
+            ->with('html//h3')
+            ->will($this->returnValue('h3'));
+
+        $this->assertEquals('h3', $node->getTagName());
+    }
+
+    public function testGetParent()
+    {
+        $node = new NodeElement('elem', $this->session);
+        $parent = $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->driver
+            ->expects($this->once())
+            ->method('find')
+            ->with('elem/..')
+            ->will($this->returnValue(array($parent)));
+
+        $this->selectors
+            ->expects($this->once())
+            ->method('selectorToXpath')
+            ->with('xpath', '..')
+            ->will($this->returnValue('..'));
+
+        $this->assertSame($parent, $node->getParent());
+    }
+
+    public function testAttachFile()
+    {
+        $node = new NodeElement('elem', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('attachFile')
+            ->with('elem', 'path');
+
+        $node->attachFile('path');
+    }
+
+    public function testIsVisible()
+    {
+        $node = new NodeElement('some_xpath', $this->session);
+
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('isVisible')
+            ->with('some_xpath')
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->assertTrue($node->isVisible());
+        $this->assertFalse($node->isVisible());
+    }
+
+    public function testIsChecked()
+    {
+        $node = new NodeElement('some_xpath', $this->session);
+
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('isChecked')
+            ->with('some_xpath')
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->assertTrue($node->isChecked());
+        $this->assertFalse($node->isChecked());
+    }
+
+    public function testIsSelected()
+    {
+        $node = new NodeElement('some_xpath', $this->session);
+
+        $this->driver
+            ->expects($this->exactly(2))
+            ->method('isSelected')
+            ->with('some_xpath')
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->assertTrue($node->isSelected());
+        $this->assertFalse($node->isSelected());
+    }
+
+    public function testFocus()
+    {
+        $node = new NodeElement('some-element', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('focus')
+            ->with('some-element');
+
+        $node->focus();
+    }
+
+    public function testBlur()
+    {
+        $node = new NodeElement('some-element', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('blur')
+            ->with('some-element');
+
+        $node->blur();
+    }
+
+    public function testMouseOver()
+    {
+        $node = new NodeElement('some-element', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('mouseOver')
+            ->with('some-element');
+
+        $node->mouseOver();
+    }
+
+    public function testDragTo()
+    {
+        $node = new NodeElement('some_tag1', $this->session);
+
+        $target = $this->getMock('Behat\Mink\Element\ElementInterface');
+        $target->expects($this->any())
+            ->method('getXPath')
+            ->will($this->returnValue('some_tag2'));
+
+        $this->driver
+            ->expects($this->once())
+            ->method('dragTo')
+            ->with('some_tag1', 'some_tag2');
+
+        $node->dragTo($target);
+    }
+
+    public function testKeyPress()
+    {
+        $node = new NodeElement('elem', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('keyPress')
+            ->with('elem', 'key');
+
+        $node->keyPress('key');
+    }
+
+    public function testKeyDown()
+    {
+        $node = new NodeElement('elem', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('keyDown')
+            ->with('elem', 'key');
+
+        $node->keyDown('key');
+    }
+
+    public function testKeyUp()
+    {
+        $node = new NodeElement('elem', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('keyUp')
+            ->with('elem', 'key');
+
+        $node->keyUp('key');
+    }
+
+    public function testSubmitForm()
+    {
+        $node = new NodeElement('some_xpath', $this->session);
+
+        $this->driver
+            ->expects($this->once())
+            ->method('submitForm')
+            ->with('some_xpath');
+
+        $node->submit();
+    }
+
+    public function testFindAllUnion()
+    {
+        $node = new NodeElement('some_xpath', $this->session);
+        $xpath = "some_tag1 | some_tag2[@foo =\n 'bar|'']\n | some_tag3[foo | bar]";
+        $expected = "some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|''] | some_xpath/some_tag3[foo | bar]";
+
+        $this->driver
+            ->expects($this->exactly(1))
+            ->method('find')
+            ->will($this->returnValueMap(array(
+                array($expected, array(2, 3, 4)),
+            )));
+
+        $this->selectors
+            ->expects($this->exactly(1))
+            ->method('selectorToXpath')
+            ->will($this->returnValueMap(array(
+                array('xpath', $xpath, $xpath),
+            )));
+
+        $this->assertEquals(3, count($node->findAll('xpath', $xpath)));
+    }
+
+    public function testFindAllParentUnion()
+    {
+        $node = new NodeElement('some_xpath | another_xpath', $this->session);
+        $xpath = "some_tag1 | some_tag2";
+        $expectedPrefixed = "(some_xpath | another_xpath)/some_tag1 | (some_xpath | another_xpath)/some_tag2";
+
+        $this->driver
+            ->expects($this->exactly(1))
+            ->method('find')
+            ->will($this->returnValueMap(array(
+                array($expectedPrefixed, array(2, 3, 4)),
+            )));
+
+        $this->selectors
+            ->expects($this->exactly(1))
+            ->method('selectorToXpath')
+            ->will($this->returnValueMap(array(
+                array('xpath', $xpath, $xpath),
+            )));
+
+        $this->assertEquals(3, count($node->findAll('xpath', $xpath)));
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Exception/ElementExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementExceptionTest.php
new file mode 100644
index 0000000..021e8d5
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Exception/ElementExceptionTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Behat\Mink\Tests\Exception;
+
+use Behat\Mink\Exception\ElementException;
+
+class ElementExceptionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testMessage()
+    {
+        $exception = new ElementException($this->getElementMock(), new \Exception('Something went wrong'));
+
+        $expectedMessage = "Exception thrown by element XPath\nSomething went wrong";
+        $this->assertEquals($expectedMessage, $exception->getMessage());
+        $this->assertEquals($expectedMessage, (string) $exception);
+    }
+
+    public function testElement()
+    {
+        $element = $this->getElementMock();
+
+        $exception = new ElementException($element, new \Exception('Something went wrong'));
+
+        $this->assertSame($element, $exception->getElement());
+    }
+
+    private function getElementMock()
+    {
+        $mock = $this->getMockBuilder('Behat\Mink\Element\Element')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $mock->expects($this->any())
+            ->method('getXPath')
+            ->will($this->returnValue('element XPath'));
+
+        return $mock;
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Exception/ElementHtmlExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementHtmlExceptionTest.php
new file mode 100644
index 0000000..63d39dd
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Exception/ElementHtmlExceptionTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Behat\Mink\Tests\Exception;
+
+use Behat\Mink\Exception\ElementHtmlException;
+
+class ElementHtmlExceptionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testExceptionToString()
+    {
+        $driver = $this->getMock('Behat\Mink\Driver\DriverInterface');
+        $element = $this->getElementMock();
+
+        $session = $this->getSessionMock();
+        $session->expects($this->any())
+            ->method('getDriver')
+            ->will($this->returnValue($driver));
+        $session->expects($this->any())
+            ->method('getStatusCode')
+            ->will($this->returnValue(200));
+        $session->expects($this->any())
+            ->method('getCurrentUrl')
+            ->will($this->returnValue('http://localhost/test'));
+
+        $element->expects($this->any())
+            ->method('getOuterHtml')
+            ->will($this->returnValue("<div>\n    <h1>Hello world</h1>\n    <p>Test</p>\n</div>"));
+
+        $expected = <<<'TXT'
+Html error
+
++--[ HTTP/1.1 200 | http://localhost/test | %s ]
+|
+|  <div>
+|      <h1>Hello world</h1>
+|      <p>Test</p>
+|  </div>
+|
+TXT;
+
+        $expected = sprintf($expected.'  ', get_class($driver));
+
+        $exception = new ElementHtmlException('Html error', $session, $element);
+
+        $this->assertEquals($expected, $exception->__toString());
+    }
+
+    private function getSessionMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Session')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+
+    private function getElementMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Exception/ElementNotFoundExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementNotFoundExceptionTest.php
new file mode 100644
index 0000000..8cb4e5d
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Exception/ElementNotFoundExceptionTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Behat\Mink\Tests\Exception;
+
+use Behat\Mink\Exception\ElementNotFoundException;
+
+class ElementNotFoundExceptionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider provideExceptionMessage
+     */
+    public function testBuildMessage($message, $type, $selector = null, $locator = null)
+    {
+        $session = $this->getMockBuilder('Behat\Mink\Session')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $exception = new ElementNotFoundException($session, $type, $selector, $locator);
+
+        $this->assertEquals($message, $exception->getMessage());
+    }
+
+    public function provideExceptionMessage()
+    {
+        return array(
+            array('Tag not found.', null),
+            array('Field not found.', 'field'),
+            array('Tag matching locator "foobar" not found.', null, null, 'foobar'),
+            array('Tag matching css "foobar" not found.', null, 'css', 'foobar'),
+            array('Field matching xpath "foobar" not found.', 'Field', 'xpath', 'foobar'),
+            array('Tag with name "foobar" not found.', null, 'name', 'foobar'),
+        );
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Exception/ElementTextExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementTextExceptionTest.php
new file mode 100644
index 0000000..73cef5b
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Exception/ElementTextExceptionTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Behat\Mink\Tests\Exception;
+
+use Behat\Mink\Exception\ElementTextException;
+
+class ElementTextExceptionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testExceptionToString()
+    {
+        $driver = $this->getMock('Behat\Mink\Driver\DriverInterface');
+        $element = $this->getElementMock();
+
+        $session = $this->getSessionMock();
+        $session->expects($this->any())
+            ->method('getDriver')
+            ->will($this->returnValue($driver));
+        $session->expects($this->any())
+            ->method('getStatusCode')
+            ->will($this->returnValue(200));
+        $session->expects($this->any())
+            ->method('getCurrentUrl')
+            ->will($this->returnValue('http://localhost/test'));
+
+        $element->expects($this->any())
+            ->method('getText')
+            ->will($this->returnValue("Hello world\nTest\n"));
+
+        $expected = <<<'TXT'
+Text error
+
++--[ HTTP/1.1 200 | http://localhost/test | %s ]
+|
+|  Hello world
+|  Test
+|
+TXT;
+
+        $expected = sprintf($expected.'  ', get_class($driver));
+
+        $exception = new ElementTextException('Text error', $session, $element);
+
+        $this->assertEquals($expected, $exception->__toString());
+    }
+
+    private function getSessionMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Session')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+
+    private function getElementMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Element\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Exception/ExpectationExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ExpectationExceptionTest.php
new file mode 100644
index 0000000..23e53bc
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Exception/ExpectationExceptionTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Behat\Mink\Tests\Exception;
+
+use Behat\Mink\Exception\ExpectationException;
+
+class ExpectationExceptionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testEmptyMessageAndPreviousException()
+    {
+        $exception = new ExpectationException('', $this->getSessionMock(), new \Exception('Something failed'));
+
+        $this->assertEquals('Something failed', $exception->getMessage());
+    }
+
+    public function testExceptionToString()
+    {
+        $driver = $this->getMock('Behat\Mink\Driver\DriverInterface');
+        $page = $this->getPageMock();
+
+        $session = $this->getSessionMock();
+        $session->expects($this->any())
+            ->method('getDriver')
+            ->will($this->returnValue($driver));
+        $session->expects($this->any())
+            ->method('getPage')
+            ->will($this->returnValue($page));
+        $session->expects($this->any())
+            ->method('getStatusCode')
+            ->will($this->returnValue(200));
+        $session->expects($this->any())
+            ->method('getCurrentUrl')
+            ->will($this->returnValue('http://localhost/test'));
+
+        $html = "<html><head><title>Hello</title></head>\n<body>\n<h1>Hello world</h1>\n<p>Test</p>\n</body></html>";
+        $page->expects($this->any())
+            ->method('getContent')
+            ->will($this->returnValue($html));
+
+        $expected = <<<'TXT'
+Expectation failure
+
++--[ HTTP/1.1 200 | http://localhost/test | %s ]
+|
+|  <body>
+|  <h1>Hello world</h1>
+|  <p>Test</p>
+|  </body>
+|
+TXT;
+
+        $expected = sprintf($expected.'  ', get_class($driver));
+
+        $exception = new ExpectationException('Expectation failure', $session);
+
+        $this->assertEquals($expected, $exception->__toString());
+    }
+
+    public function testExceptionWhileRenderingString()
+    {
+        $session = $this->getSessionMock();
+        $session->expects($this->any())
+            ->method('getPage')
+            ->will($this->throwException(new \Exception('Broken page')));
+
+        $exception = new ExpectationException('Expectation failure', $session);
+
+        $this->assertEquals('Expectation failure', $exception->__toString());
+    }
+
+    private function getSessionMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Session')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+
+    private function getPageMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Element\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Exception/ResponseTextExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ResponseTextExceptionTest.php
new file mode 100644
index 0000000..4651b7c
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Exception/ResponseTextExceptionTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Behat\Mink\Tests\Exception;
+
+use Behat\Mink\Exception\ResponseTextException;
+
+class ResponseTextExceptionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testExceptionToString()
+    {
+        $driver = $this->getMock('Behat\Mink\Driver\DriverInterface');
+        $page = $this->getPageMock();
+
+        $session = $this->getSessionMock();
+        $session->expects($this->any())
+            ->method('getDriver')
+            ->will($this->returnValue($driver));
+        $session->expects($this->any())
+            ->method('getPage')
+            ->will($this->returnValue($page));
+        $session->expects($this->any())
+            ->method('getStatusCode')
+            ->will($this->returnValue(200));
+        $session->expects($this->any())
+            ->method('getCurrentUrl')
+            ->will($this->returnValue('http://localhost/test'));
+
+        $page->expects($this->any())
+            ->method('getText')
+            ->will($this->returnValue("Hello world\nTest\n"));
+
+        $expected = <<<'TXT'
+Text error
+
++--[ HTTP/1.1 200 | http://localhost/test | %s ]
+|
+|  Hello world
+|  Test
+|
+TXT;
+
+        $expected = sprintf($expected.'  ', get_class($driver));
+
+        $exception = new ResponseTextException('Text error', $session);
+
+        $this->assertEquals($expected, $exception->__toString());
+    }
+
+    private function getSessionMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Session')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+
+    private function getPageMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Element\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+}
diff --git a/core/vendor/behat/mink/tests/MinkTest.php b/core/vendor/behat/mink/tests/MinkTest.php
new file mode 100644
index 0000000..11d6466
--- /dev/null
+++ b/core/vendor/behat/mink/tests/MinkTest.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace Behat\Mink\Tests;
+
+use Behat\Mink\Mink;
+
+class MinkTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var Mink
+     */
+    private $mink;
+
+    protected function setUp()
+    {
+        $this->mink = new Mink();
+    }
+
+    public function testRegisterSession()
+    {
+        $session = $this->getSessionMock();
+
+        $this->assertFalse($this->mink->hasSession('not_registered'));
+        $this->assertFalse($this->mink->hasSession('js'));
+        $this->assertFalse($this->mink->hasSession('my'));
+
+        $this->mink->registerSession('my', $session);
+
+        $this->assertTrue($this->mink->hasSession('my'));
+        $this->assertFalse($this->mink->hasSession('not_registered'));
+        $this->assertFalse($this->mink->hasSession('js'));
+    }
+
+    public function testRegisterSessionThroughConstructor()
+    {
+        $mink = new Mink(array('my' => $this->getSessionMock()));
+
+        $this->assertTrue($mink->hasSession('my'));
+    }
+
+    public function testSessionAutostop()
+    {
+        $session1 = $this->getSessionMock();
+        $session2 = $this->getSessionMock();
+        $this->mink->registerSession('my1', $session1);
+        $this->mink->registerSession('my2', $session2);
+
+        $session1
+            ->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(true));
+        $session1
+            ->expects($this->once())
+            ->method('stop');
+        $session2
+            ->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(false));
+        $session2
+            ->expects($this->never())
+            ->method('stop');
+
+        unset($this->mink);
+    }
+
+    public function testNotStartedSession()
+    {
+        $session = $this->getSessionMock();
+
+        $session
+            ->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(false));
+        $session
+            ->expects($this->once())
+            ->method('start');
+
+        $this->mink->registerSession('mock_session', $session);
+        $this->assertSame($session, $this->mink->getSession('mock_session'));
+
+        $this->setExpectedException('InvalidArgumentException');
+
+        $this->mink->getSession('not_registered');
+    }
+
+    public function testGetAlreadyStartedSession()
+    {
+        $session = $this->getSessionMock();
+
+        $session
+            ->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(true));
+        $session
+            ->expects($this->never())
+            ->method('start');
+
+        $this->mink->registerSession('mock_session', $session);
+        $this->assertSame($session, $this->mink->getSession('mock_session'));
+    }
+
+    public function testSetDefaultSessionName()
+    {
+        $this->assertNull($this->mink->getDefaultSessionName());
+
+        $session = $this->getSessionMock();
+        $this->mink->registerSession('session_name', $session);
+        $this->mink->setDefaultSessionName('session_name');
+
+        $this->assertEquals('session_name', $this->mink->getDefaultSessionName());
+
+        $this->setExpectedException('InvalidArgumentException');
+
+        $this->mink->setDefaultSessionName('not_registered');
+    }
+
+    public function testGetDefaultSession()
+    {
+        $session1 = $this->getSessionMock();
+        $session2 = $this->getSessionMock();
+
+        $this->assertNotSame($session1, $session2);
+
+        $this->mink->registerSession('session_1', $session1);
+        $this->mink->registerSession('session_2', $session2);
+        $this->mink->setDefaultSessionName('session_2');
+
+        $this->assertSame($session1, $this->mink->getSession('session_1'));
+        $this->assertSame($session2, $this->mink->getSession('session_2'));
+        $this->assertSame($session2, $this->mink->getSession());
+
+        $this->mink->setDefaultSessionName('session_1');
+
+        $this->assertSame($session1, $this->mink->getSession());
+    }
+
+    public function testGetNoDefaultSession()
+    {
+        $session1 = $this->getSessionMock();
+
+        $this->mink->registerSession('session_1', $session1);
+
+        $this->setExpectedException('InvalidArgumentException');
+
+        $this->mink->getSession();
+    }
+
+    public function testIsSessionStarted()
+    {
+        $session_1 = $this->getSessionMock();
+        $session_2 = $this->getSessionMock();
+
+        $session_1
+            ->expects($this->any())
+            ->method('isStarted')
+            ->will($this->returnValue(false));
+        $session_1
+            ->expects($this->never())
+            ->method('start');
+
+        $session_2
+            ->expects($this->any())
+            ->method('isStarted')
+            ->will($this->returnValue(true));
+        $session_2
+            ->expects($this->never())
+            ->method('start');
+
+        $this->mink->registerSession('not_started', $session_1);
+        $this->assertFalse($this->mink->isSessionStarted('not_started'));
+
+        $this->mink->registerSession('started', $session_2);
+        $this->assertTrue($this->mink->isSessionStarted('started'));
+
+        $this->setExpectedException('InvalidArgumentException');
+
+        $this->mink->getSession('not_registered');
+    }
+
+    public function testAssertSession()
+    {
+        $session = $this->getSessionMock();
+
+        $this->mink->registerSession('my', $session);
+
+        $this->assertInstanceOf('Behat\Mink\WebAssert', $this->mink->assertSession('my'));
+    }
+
+    public function testAssertSessionNotRegistered()
+    {
+        $session = $this->getSessionMock();
+
+        $this->assertInstanceOf('Behat\Mink\WebAssert', $this->mink->assertSession($session));
+    }
+
+    public function testResetSessions()
+    {
+        $session1 = $this->getSessionMock();
+        $session1->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(false));
+        $session1->expects($this->never())
+            ->method('reset');
+
+        $session2 = $this->getSessionMock();
+        $session2->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(true));
+        $session2->expects($this->once())
+            ->method('reset');
+
+        $this->mink->registerSession('not started', $session1);
+        $this->mink->registerSession('started', $session2);
+
+        $this->mink->resetSessions();
+    }
+
+    public function testRestartSessions()
+    {
+        $session1 = $this->getSessionMock();
+        $session1->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(false));
+        $session1->expects($this->never())
+            ->method('restart');
+
+        $session2 = $this->getSessionMock();
+        $session2->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(true));
+        $session2->expects($this->once())
+            ->method('restart');
+
+        $this->mink->registerSession('not started', $session1);
+        $this->mink->registerSession('started', $session2);
+
+        $this->mink->restartSessions();
+    }
+
+    private function getSessionMock()
+    {
+        return $this->getMockBuilder('Behat\Mink\Session')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Selector/CssSelectorTest.php b/core/vendor/behat/mink/tests/Selector/CssSelectorTest.php
new file mode 100644
index 0000000..ea80d93
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/CssSelectorTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Behat\Mink\Tests\Selector;
+
+use Behat\Mink\Selector\CssSelector;
+
+class CssSelectorTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\CssSelector\CssSelector')) {
+            $this->markTestSkipped('Symfony2 CssSelector component not installed');
+        }
+    }
+
+    public function testSelector()
+    {
+        $selector = new CssSelector();
+
+        $this->assertEquals('descendant-or-self::h3', $selector->translateToXPath('h3'));
+        $this->assertEquals('descendant-or-self::h3/span', $selector->translateToXPath('h3 > span'));
+
+        if (interface_exists('Symfony\Component\CssSelector\XPath\TranslatorInterface')) {
+            // The rewritten component of Symfony 2.3 checks for attribute existence first for the class.
+            $expectation = "descendant-or-self::h3/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' my_div ')]";
+        } else {
+            $expectation = "descendant-or-self::h3/*[contains(concat(' ', normalize-space(@class), ' '), ' my_div ')]";
+        }
+        $this->assertEquals($expectation, $selector->translateToXPath('h3 > .my_div'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testThrowsForArrayLocator()
+    {
+        $selector = new CssSelector();
+
+        $selector->translateToXPath(array('h3'));
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Selector/ExactNamedSelectorTest.php b/core/vendor/behat/mink/tests/Selector/ExactNamedSelectorTest.php
new file mode 100644
index 0000000..df51f0f
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/ExactNamedSelectorTest.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Behat\Mink\Tests\Selector;
+
+use Behat\Mink\Selector\ExactNamedSelector;
+
+class ExactNamedSelectorTest extends NamedSelectorTest
+{
+    protected function getSelector()
+    {
+        return new ExactNamedSelector();
+    }
+
+    protected function allowPartialMatch()
+    {
+        return false;
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Selector/NamedSelectorTest.php b/core/vendor/behat/mink/tests/Selector/NamedSelectorTest.php
new file mode 100644
index 0000000..1ce2faa
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/NamedSelectorTest.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Behat\Mink\Tests\Selector;
+
+use Behat\Mink\Selector\NamedSelector;
+use Behat\Mink\Selector\SelectorsHandler;
+
+abstract class NamedSelectorTest extends \PHPUnit_Framework_TestCase
+{
+    public function testRegisterXpath()
+    {
+        $selector = $this->getSelector();
+
+        $selector->registerNamedXpath('some', 'my_xpath');
+        $this->assertEquals('my_xpath', $selector->translateToXPath('some'));
+
+        $this->setExpectedException('InvalidArgumentException');
+
+        $selector->translateToXPath('custom');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testInvalidLocator()
+    {
+        $namedSelector = $this->getSelector();
+
+        $namedSelector->translateToXPath(array('foo', 'bar', 'baz'));
+    }
+
+    /**
+     * @dataProvider getSelectorTests
+     */
+    public function testSelectors($fixtureFile, $selector, $locator, $expectedExactCount, $expectedPartialCount = null)
+    {
+        $expectedCount = $this->allowPartialMatch() && null !== $expectedPartialCount
+            ? $expectedPartialCount
+            : $expectedExactCount;
+
+        $dom = new \DOMDocument('1.0', 'UTF-8');
+        $dom->loadHTMLFile(__DIR__.'/fixtures/'.$fixtureFile);
+
+        // Escape the locator as Mink 1.x expects the caller of the NamedSelector to handle it
+        $selectorsHandler = new SelectorsHandler();
+        $locator = $selectorsHandler->xpathLiteral($locator);
+
+        $namedSelector = $this->getSelector();
+
+        $xpath = $namedSelector->translateToXPath(array($selector, $locator));
+
+        $domXpath = new \DOMXPath($dom);
+        $nodeList = $domXpath->query($xpath);
+
+        $this->assertEquals($expectedCount, $nodeList->length);
+    }
+
+    public function getSelectorTests()
+    {
+        $fieldCount = 8; // fields without `type` attribute
+        $fieldCount += 4; // fields with `type=checkbox` attribute
+        $fieldCount += 4; // fields with `type=radio` attribute
+        $fieldCount += 4; // fields with `type=file` attribute
+
+        // Fixture file,  selector name,  locator,  expected number of exact matched elements, expected number of partial matched elements if different
+        return array(
+            'fieldset' => array('test.html', 'fieldset', 'fieldset-text', 2, 3),
+
+            'field (name/placeholder/label)' => array('test.html', 'field', 'the-field', $fieldCount),
+            'field (input, with-id)' => array('test.html', 'field', 'the-field-input', 1),
+            'field (textarea, with-id)' => array('test.html', 'field', 'the-field-textarea', 1),
+            'field (select, with-id)' => array('test.html', 'field', 'the-field-select', 1),
+            'field (input type=submit, with-id) ignored' => array('test.html', 'field', 'the-field-submit-button', 0),
+            'field (input type=image, with-id) ignored' => array('test.html', 'field', 'the-field-image-button', 0),
+            'field (input type=button, with-id) ignored' => array('test.html', 'field', 'the-field-button-button', 0),
+            'field (input type=reset, with-id) ignored' => array('test.html', 'field', 'the-field-reset-button', 0),
+            'field (input type=hidden, with-id) ignored' => array('test.html', 'field', 'the-field-hidden', 0),
+
+            'link (with-href)' => array('test.html', 'link', 'link-text', 5, 9),
+            'link (without-href) ignored' => array('test.html', 'link', 'bad-link-text', 0),
+            'link* (role=link)' => array('test.html', 'link', 'link-role-text', 4, 7),
+
+            'button (input, name/value/title)' => array('test.html', 'button', 'button-text', 25, 42),
+            'button (type=image, with-alt)' => array('test.html', 'button', 'button-alt-text', 1, 2),
+            'button (input type=submit, with-id)' => array('test.html', 'button', 'input-submit-button', 1),
+            'button (input type=image, with-id)' => array('test.html', 'button', 'input-image-button', 1),
+            'button (input type=button, with-id)' => array('test.html', 'button', 'input-button-button', 1),
+            'button (input type=reset, with-id)' => array('test.html', 'button', 'input-reset-button', 1),
+            'button (button type=submit, with-id)' => array('test.html', 'button', 'button-submit-button', 1),
+            'button (button type=image, with-id)' => array('test.html', 'button', 'button-image-button', 1),
+            'button (button type=button, with-id)' => array('test.html', 'button', 'button-button-button', 1),
+            'button (button type=reset, with-id)' => array('test.html', 'button', 'button-reset-button', 1),
+            'button* (role=button, name/value/title)' => array('test.html', 'button', 'button-role-text', 12, 20),
+            'button* (role=button type=submit, with-id)' => array('test.html', 'button', 'role-button-submit-button', 1),
+            'button* (role=button type=image, with-id)' => array('test.html', 'button', 'role-button-image-button', 1),
+            'button* (role=button type=button, with-id)' => array('test.html', 'button', 'role-button-button-button', 1),
+            'button* (role=button type=reset, with-id)' => array('test.html', 'button', 'role-button-reset-button', 1),
+
+            'link_or_button (with-href)' => array('test.html', 'link_or_button', 'link-text', 5, 9),
+            'link_or_button (without-href) ignored' => array('test.html', 'link_or_button', 'bad-link-text', 0),
+            'link_or_button* (role=link)' => array('test.html', 'link_or_button', 'link-role-text', 4, 7),
+
+            // bug in selector: 17 instead of 25 and 34 instead of 42, because 8 buttons with `name` attribute were not matched
+            'link_or_button (input, name/value/title)' => array('test.html', 'link_or_button', 'button-text', 17, 34),
+            'link_or_button (type=image, with-alt)' => array('test.html', 'link_or_button', 'button-alt-text', 1, 2),
+            'link_or_button (input type=submit, with-id)' => array('test.html', 'link_or_button', 'input-submit-button', 1),
+            'link_or_button (input type=image, with-id)' => array('test.html', 'link_or_button', 'input-image-button', 1),
+            'link_or_button (input type=button, with-id)' => array('test.html', 'link_or_button', 'input-button-button', 1),
+            'link_or_button (input type=reset, with-id)' => array('test.html', 'link_or_button', 'input-reset-button', 1),
+            'link_or_button (button type=submit, with-id)' => array('test.html', 'link_or_button', 'button-submit-button', 1),
+            'link_or_button (button type=image, with-id)' => array('test.html', 'link_or_button', 'button-image-button', 1),
+            'link_or_button (button type=button, with-id)' => array('test.html', 'link_or_button', 'button-button-button', 1),
+            'link_or_button (button type=reset, with-id)' => array('test.html', 'link_or_button', 'button-reset-button', 1),
+
+            // bug in selector: 8 instead of 12 and 16 instead of 20, because 4 buttons with `name` attribute were not matched
+            'link_or_button* (role=button, name/value/title)' => array('test.html', 'link_or_button', 'button-role-text', 8, 16),
+            'link_or_button* (role=button type=submit, with-id)' => array('test.html', 'link_or_button', 'role-button-submit-button', 1),
+            'link_or_button* (role=button type=image, with-id)' => array('test.html', 'link_or_button', 'role-button-image-button', 1),
+            'link_or_button* (role=button type=button, with-id)' => array('test.html', 'link_or_button', 'role-button-button-button', 1),
+            'link_or_button* (role=button type=reset, with-id)' => array('test.html', 'link_or_button', 'role-button-reset-button', 1),
+
+            // 3 matches, because matches every HTML node in path: html > body > div
+            'content' => array('test.html', 'content', 'content-text', 1, 4),
+
+            'select (name/label)' => array('test.html', 'select', 'the-field', 3),
+            'select (with-id)' => array('test.html', 'select', 'the-field-select', 1),
+
+            'checkbox (name/label)' => array('test.html', 'checkbox', 'the-field', 3),
+            'checkbox (with-id)' => array('test.html', 'checkbox', 'the-field-checkbox', 1),
+
+            'radio (name/label)' => array('test.html', 'radio', 'the-field', 3),
+            'radio (with-id)' => array('test.html', 'radio', 'the-field-radio', 1),
+
+            'file (name/label)' => array('test.html', 'file', 'the-field', 3),
+            'file (with-id)' => array('test.html', 'file', 'the-field-file', 1),
+
+            'optgroup' => array('test.html', 'optgroup', 'group-label', 1, 2),
+
+            'option' => array('test.html', 'option', 'option-value', 2, 3),
+
+            'table' => array('test.html', 'table', 'the-table', 2, 3),
+
+            'id' => array('test.html', 'id', 'bad-link-text', 1),
+            'id or name' => array('test.html', 'id_or_name', 'the-table', 2),
+        );
+    }
+
+    /**
+     * @return NamedSelector
+     */
+    abstract protected function getSelector();
+
+    /**
+     * @return boolean
+     */
+    abstract protected function allowPartialMatch();
+}
diff --git a/core/vendor/behat/mink/tests/Selector/PartialNamedSelectorTest.php b/core/vendor/behat/mink/tests/Selector/PartialNamedSelectorTest.php
new file mode 100644
index 0000000..c3e631a
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/PartialNamedSelectorTest.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Behat\Mink\Tests\Selector;
+
+use Behat\Mink\Selector\PartialNamedSelector;
+
+class PartialNamedSelectorTest extends NamedSelectorTest
+{
+    protected function getSelector()
+    {
+        return new PartialNamedSelector();
+    }
+
+    protected function allowPartialMatch()
+    {
+        return true;
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Selector/SelectorsHandlerTest.php b/core/vendor/behat/mink/tests/Selector/SelectorsHandlerTest.php
new file mode 100644
index 0000000..8bcb379
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/SelectorsHandlerTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Behat\Mink\Tests\Selector;
+
+use Behat\Mink\Selector\SelectorsHandler;
+
+class SelectorsHandlerTest extends \PHPUnit_Framework_TestCase
+{
+    public function testRegisterSelector()
+    {
+        $selector = $this->getMockBuilder('Behat\Mink\Selector\SelectorInterface')->getMock();
+        $handler = new SelectorsHandler();
+
+        $this->assertFalse($handler->isSelectorRegistered('custom'));
+
+        $handler->registerSelector('custom', $selector);
+
+        $this->assertTrue($handler->isSelectorRegistered('custom'));
+        $this->assertSame($selector, $handler->getSelector('custom'));
+    }
+
+    public function testRegisterSelectorThroughConstructor()
+    {
+        $selector = $this->getMockBuilder('Behat\Mink\Selector\SelectorInterface')->getMock();
+        $handler = new SelectorsHandler(array('custom' => $selector));
+
+        $this->assertTrue($handler->isSelectorRegistered('custom'));
+        $this->assertSame($selector, $handler->getSelector('custom'));
+    }
+
+    public function testRegisterDefaultSelectors()
+    {
+        $handler = new SelectorsHandler();
+
+        $this->assertTrue($handler->isSelectorRegistered('css'));
+        $this->assertTrue($handler->isSelectorRegistered('named_exact'));
+        $this->assertTrue($handler->isSelectorRegistered('named_partial'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testXpathSelectorThrowsExceptionForArrayLocator()
+    {
+        $handler = new SelectorsHandler();
+        $handler->selectorToXpath('xpath', array('some_xpath'));
+    }
+
+    public function testXpathSelectorIsReturnedAsIs()
+    {
+        $handler = new SelectorsHandler();
+        $this->assertEquals('some_xpath', $handler->selectorToXpath('xpath', 'some_xpath'));
+    }
+
+    public function testSelectorToXpath()
+    {
+        $selector = $this->getMockBuilder('Behat\Mink\Selector\SelectorInterface')->getMock();
+        $handler = new SelectorsHandler();
+
+        $handler->registerSelector('custom_selector', $selector);
+
+        $selector
+            ->expects($this->once())
+            ->method('translateToXPath')
+            ->with($locator = 'some[locator]')
+            ->will($this->returnValue($ret = '[]some[]locator'));
+
+        $this->assertEquals($ret, $handler->selectorToXpath('custom_selector', $locator));
+
+        $this->setExpectedException('InvalidArgumentException');
+        $handler->selectorToXpath('undefined', 'asd');
+    }
+
+    public function testXpathLiteral()
+    {
+        $handler = new SelectorsHandler();
+
+        $this->assertEquals("'some simple string'", $handler->xpathLiteral('some simple string'));
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Selector/Xpath/EscaperTest.php b/core/vendor/behat/mink/tests/Selector/Xpath/EscaperTest.php
new file mode 100644
index 0000000..535dac6
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/Xpath/EscaperTest.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Behat\Mink\Tests\Selector\Xpath;
+
+use Behat\Mink\Selector\Xpath\Escaper;
+
+class EscaperTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getXpathLiterals
+     */
+    public function testXpathLiteral($string, $expected)
+    {
+        $escaper = new Escaper();
+
+        $this->assertEquals($expected, $escaper->escapeLiteral($string));
+    }
+
+    public function getXpathLiterals()
+    {
+        return array(
+            array('some simple string', "'some simple string'"),
+            array('some "d-brackets" string', "'some \"d-brackets\" string'"),
+            array('some \'s-brackets\' string', "\"some 's-brackets' string\""),
+            array(
+                'some \'s-brackets\' and "d-brackets" string',
+                'concat(\'some \',"\'",\'s-brackets\',"\'",\' and "d-brackets" string\')',
+            ),
+        );
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Selector/Xpath/ManipulatorTest.php b/core/vendor/behat/mink/tests/Selector/Xpath/ManipulatorTest.php
new file mode 100644
index 0000000..7c20561
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/Xpath/ManipulatorTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Behat\Mink\Tests\Selector\Xpath;
+
+use Behat\Mink\Selector\Xpath\Manipulator;
+
+class ManipulatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getPrependedXpath
+     */
+    public function testPrepend($prefix, $xpath, $expectedXpath)
+    {
+        $manipulator = new Manipulator();
+
+        $this->assertEquals($expectedXpath, $manipulator->prepend($xpath, $prefix));
+    }
+
+    public function getPrependedXpath()
+    {
+        return array(
+            'simple' => array(
+                'some_xpath',
+                'some_tag1',
+                'some_xpath/some_tag1',
+            ),
+            'with slash' => array(
+                'some_xpath',
+                '/some_tag1',
+                'some_xpath/some_tag1',
+            ),
+            'union' => array(
+                'some_xpath',
+                'some_tag1 | some_tag2',
+                'some_xpath/some_tag1 | some_xpath/some_tag2',
+            ),
+            'wrapped union' => array(
+                'some_xpath',
+                '(some_tag1 | some_tag2)/some_child',
+                '(some_xpath/some_tag1 | some_xpath/some_tag2)/some_child',
+            ),
+            'multiple wrapped union' => array(
+                'some_xpath',
+                '( ( some_tag1 | some_tag2)/some_child | some_tag3)/leaf',
+                '( ( some_xpath/some_tag1 | some_xpath/some_tag2)/some_child | some_xpath/some_tag3)/leaf',
+            ),
+            'parent union' => array(
+                'some_xpath | another_xpath',
+                'some_tag1 | some_tag2',
+                '(some_xpath | another_xpath)/some_tag1 | (some_xpath | another_xpath)/some_tag2',
+            ),
+            'complex condition' => array(
+                'some_xpath',
+                'some_tag1 | some_tag2[@foo = "bar|"] | some_tag3[foo | bar]',
+                'some_xpath/some_tag1 | some_xpath/some_tag2[@foo = "bar|"] | some_xpath/some_tag3[foo | bar]',
+            ),
+            'multiline' => array(
+                'some_xpath',
+                "some_tag1 | some_tag2[@foo =\n 'bar|'']\n | some_tag3[foo | bar]",
+                "some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|''] | some_xpath/some_tag3[foo | bar]",
+            ),
+        );
+    }
+}
diff --git a/core/vendor/behat/mink/tests/Selector/fixtures/test.html b/core/vendor/behat/mink/tests/Selector/fixtures/test.html
new file mode 100644
index 0000000..c64ae10
--- /dev/null
+++ b/core/vendor/behat/mink/tests/Selector/fixtures/test.html
@@ -0,0 +1,310 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title></title>
+</head>
+<body>
+    <div id="test-for-link-selector">
+        <!-- match links with `href` attribute -->
+        <a href="#" id="link-text"></a>
+        <a href="#">link-text</a>
+        <a href="#" title="link-text"></a>
+        <a href="#" rel="link-text"></a>
+        <a href="#">
+            <img src="#" alt="link-text"/>
+        </a>
+        <!-- partial match -->
+        <a href="#">some link-text</a>
+        <a href="#" title="some link-text"></a>
+        <a href="#" rel="some link-text"></a>
+        <a href="#">
+            <img src="#" alt="some link-text"/>
+        </a>
+
+        <!-- don't match links without `href` attribute -->
+        <a id="bad-link-text"></a>
+        <a>bad-link-text</a>
+        <a title="bad-link-text"></a>
+        <a rel="bad-link-text"></a>
+        <a>
+            <img src="#" alt="bad-link-text"/>
+        </a>
+
+        <!-- match links with `role=link` attribute -->
+        <span role="Link" id="link-role-text"></span>
+        <span role="lInk" value="link-role-text"></span>
+        <span role="liNk" title="link-role-text"></span>
+        <span role="linK">link-role-text</span>
+        <!-- partial match -->
+        <span role="link" value="some link-role-text"></span>
+        <span role="link" title="some link-role-text"></span>
+        <span role="link">some link-role-text</span>
+    </div>
+
+    <div id="test-for-fieldset-selector">
+        <!-- match fieldsets -->
+        <fieldset id="fieldset-text"></fieldset>
+
+        <fieldset>
+            <legend>fieldset-text</legend>
+        </fieldset>
+        <!-- partial match -->
+        <fieldset>
+            <legend>fieldset-text sample</legend>
+        </fieldset>
+
+        <!-- don't match fieldsets -->
+        <fieldset>fieldset-text</fieldset>
+        <fieldset></fieldset>
+    </div>
+
+    <div id="test-for-content-selector">
+        content-text
+    </div>
+    <!-- partial match -->
+    <div id="test-for-partial-content-selector">
+        some content-text
+    </div>
+
+    <form>
+        <div id="test-for-field-selector">
+            <!-- match fields by `id` attribute -->
+            <input id="the-field-input"/>
+            <textarea id="the-field-textarea"></textarea>
+            <select id="the-field-select"></select>
+
+            <!-- match fields by `name` attribute -->
+            <input name="the-field"/>
+            <textarea name="the-field"></textarea>
+            <select name="the-field"></select>
+
+            <!-- match fields by `placeholder` attribute -->
+            <input placeholder="the-field"/>
+            <textarea placeholder="the-field"></textarea>
+            <select placeholder="the-field"></select>
+
+            <!-- match fields by associated label -->
+            <label for="label-for-input">the-field</label><input id="label-for-input"/>
+            <label for="label-for-textarea">the-field</label><textarea id="label-for-textarea"></textarea>
+            <label for="label-for-select">the-field</label><select id="label-for-select"></select>
+
+            <!-- match fields, surrounded by matching label -->
+            <label>the-field<input/></label>
+            <label>the-field<textarea></textarea></label>
+            <label>the-field<select></select></label>
+
+            <!-- don't match fields by `id` attribute -->
+            <input type="Submit" id="the-field-submit-button"/>
+            <input type="iMage" id="the-field-image-button"/>
+            <input type="butTon" id="the-field-button-button"/>
+            <input type="resEt" id="the-field-reset-button"/>
+            <input type="hidDen" id="the-field-hidden"/>
+
+            <!-- don't match fields by `name` attribute -->
+            <input type="submit" name="the-field"/>
+            <input type="image" name="the-field"/>
+            <input type="button" name="the-field"/>
+            <input type="reset" name="the-field"/>
+            <input type="hidden" name="the-field"/>
+
+            <!-- don't match fields by `placeholder` attribute -->
+            <input type="submit" placeholder="the-field"/>
+            <input type="image" placeholder="the-field"/>
+            <input type="button" placeholder="the-field"/>
+            <input type="reset" placeholder="the-field"/>
+            <input type="hidden" placeholder="the-field"/>
+
+            <!-- don't match fields by associated label -->
+            <label for="label-for-the-field-submit-button">the-field</label><input type="submit" id="label-for-the-field-submit-button"/>
+            <label for="label-for-the-field-image-button">the-field</label><input type="image" id="label-for-the-field-image-button"/>
+            <label for="label-for-the-field-button-button">the-field</label><input type="button" id="label-for-the-field-button-button"/>
+            <label for="label-for-the-field-reset-button">the-field</label><input type="reset" id="label-for-the-field-reset-button"/>
+            <label for="label-for-the-field-hidden">the-field</label><input type="hidden" id="label-for-the-field-hidden"/>
+
+            <!-- don't match fields, surrounded by matching label -->
+            <label>the-field<input type="submit"/></label>
+            <label>the-field<input type="image"/></label>
+            <label>the-field<input type="button"/></label>
+            <label>the-field<input type="reset"/></label>
+            <label>the-field<input type="hidden"/></label>
+        </div>
+
+        <div id="test-for-button-selector">
+            <!-- match buttons by `id` attribute -->
+            <input type="Submit" id="input-submit-button"/>
+            <input type="iMage" id="input-image-button"/>
+            <input type="butTon" id="input-button-button"/>
+            <input type="resEt" id="input-reset-button"/>
+
+            <button type="submit" id="button-submit-button"></button>
+            <button type="image" id="button-image-button"></button>
+            <button type="button" id="button-button-button"></button>
+            <button type="reset" id="button-reset-button"></button>
+
+            <!-- match buttons by `name` attribute -->
+            <input type="submit" name="button-text"/>
+            <input type="image" name="button-text"/>
+            <input type="button" name="button-text"/>
+            <input type="reset" name="button-text"/>
+            <button type="submit" name="button-text"></button>
+            <button type="image" name="button-text"></button>
+            <button type="button" name="button-text"></button>
+            <button type="reset" name="button-text"></button>
+
+            <!-- match buttons by `value` attribute -->
+            <input type="submit" value="button-text"/>
+            <input type="image" value="button-text"/>
+            <input type="button" value="button-text"/>
+            <input type="reset" value="button-text"/>
+            <button type="submit" value="button-text"></button>
+            <button type="image" value="button-text"></button>
+            <button type="button" value="button-text"></button>
+            <button type="reset" value="button-text"></button>
+            <!-- Partial match -->
+            <input type="submit" value="some button-text"/>
+            <input type="image" value="some button-text"/>
+            <input type="button" value="some button-text"/>
+            <input type="reset" value="some button-text"/>
+            <button type="submit" value="some button-text"></button>
+            <button type="image" value="some button-text"></button>
+            <button type="button" value="some button-text"></button>
+            <button type="reset" value="some button-text"></button>
+
+            <!-- match buttons by `title` attribute -->
+            <input type="submit" title="button-text"/>
+            <input type="image" title="button-text"/>
+            <input type="button" title="button-text"/>
+            <input type="reset" title="button-text"/>
+            <button type="submit" title="button-text"></button>
+            <button type="image" title="button-text"></button>
+            <button type="button" title="button-text"></button>
+            <button type="reset" title="button-text"></button>
+            <!-- partial match -->
+            <input type="submit" title="some button-text"/>
+            <input type="image" title="some button-text"/>
+            <input type="button" title="some button-text"/>
+            <input type="reset" title="some button-text"/>
+            <button type="submit" title="some button-text"></button>
+            <button type="image" title="some button-text"></button>
+            <button type="button" title="some button-text"></button>
+            <button type="reset" title="some button-text"></button>
+
+            <!-- match some buttons by `alt` attribute -->
+            <input type="submit" alt="button-alt-text"/>
+            <input type="imaGe" alt="button-alt-text"/>
+            <input type="button" alt="button-alt-text"/>
+            <input type="reset" alt="button-alt-text"/>
+            <!-- partial match -->
+            <input type="submit" alt="some button-alt-text"/>
+            <input type="image" alt="some button-alt-text"/>
+            <input type="button" alt="some button-alt-text"/>
+            <input type="reset" alt="some button-alt-text"/>
+
+            <!-- match by `button` text -->
+            <button>button-text</button>
+            <!-- partial match -->
+            <button>some button-text</button>
+
+            <!-- match buttons with `role=button` & `id` attribute -->
+            <span role="Button" type="submit" id="role-button-submit-button"></span>
+            <span role="bUtton" type="image" id="role-button-image-button"></span>
+            <span role="buTton" type="button" id="role-button-button-button"></span>
+            <span role="butTon" type="reset" id="role-button-reset-button"></span>
+
+            <!-- match buttons with `role=button` & `name` attribute -->
+            <span role="buttOn" type="submit" name="button-role-text"></span>
+            <span role="buttoN" type="image" name="button-role-text"></span>
+            <span role="button" type="button" name="button-role-text"></span>
+            <span role="button" type="reset" name="button-role-text"></span>
+
+            <!-- match buttons with `role=button` & `value` attribute -->
+            <span role="button" type="submit" value="button-role-text"></span>
+            <span role="button" type="image" value="button-role-text"></span>
+            <span role="button" type="button" value="button-role-text"></span>
+            <span role="button" type="reset" value="button-role-text"></span>
+            <!-- partial match -->
+            <span role="button" type="submit" value="some button-role-text"></span>
+            <span role="button" type="image" value="some button-role-text"></span>
+            <span role="button" type="button" value="some button-role-text"></span>
+            <span role="button" type="reset" value="some button-role-text"></span>
+
+            <!-- match buttons with `role=button` & `title` attribute -->
+            <span role="button" type="submit" title="button-role-text"></span>
+            <span role="button" type="image" title="button-role-text"></span>
+            <span role="button" type="button" title="button-role-text"></span>
+            <span role="button" type="reset" title="button-role-text"></span>
+            <!-- partial match -->
+            <span role="button" type="submit" title="some button-role-text"></span>
+            <span role="button" type="image" title="some button-role-text"></span>
+            <span role="button" type="button" title="some button-role-text"></span>
+            <span role="button" type="reset" title="some button-role-text"></span>
+        </div>
+
+        <div id="test-for-checkbox-selector">
+            <input type="Checkbox" id="the-field-checkbox"/>
+            <input type="checkBox" name="the-field"/>
+            <input type="cheCkbox" placeholder="the-field"/>
+            <label for="label-for-checkbox">the-field</label><input type="checkboX" id="label-for-checkbox"/>
+            <label>the-field<input type="chEckbox"/></label>
+        </div>
+
+        <div id="test-for-radio-selector">
+            <input type="Radio" id="the-field-radio"/>
+            <input type="raDio" name="the-field"/>
+            <input type="radIo" placeholder="the-field"/>
+            <label for="label-for-radio">the-field</label><input type="radiO" id="label-for-radio"/>
+            <label>the-field<input type="radIo"/></label>
+        </div>
+
+        <div id="test-for-file-selector">
+            <input type="File" id="the-field-file"/>
+            <input type="fIle" name="the-field"/>
+            <input type="fiLe" placeholder="the-field"/>
+            <label for="label-for-file">the-field</label><input type="filE" id="label-for-file"/>
+            <label>the-field<input type="fiLe"/></label>
+        </div>
+
+        <div id="test-for-select-related-stuff">
+            <!-- match select stuff -->
+            <select name="the-select-stuff-test">
+                <optgroup label="group-label">
+                    <option value="option-value"></option>
+                </optgroup>
+                <option value="">option-value</option>
+                <!-- partial match -->
+                <optgroup label="some group-label">
+                    <option value="">some option-value</option>
+                </optgroup>
+            </select>
+
+            <!-- don't match select stuff -->
+            <select name="the-select-stuff-test">
+                <optgroup label="">some group-label</optgroup>
+                <option value="some option-value"></option>
+            </select>
+        </div>
+    </form>
+
+    <div id="test-for-table-selector">
+        <!-- match tables -->
+        <table id="the-table"></table>
+        <table>
+            <caption>the-table</caption>
+        </table>
+        <!-- partial match -->
+        <table>
+            <caption>some the-table</caption>
+        </table>
+
+        <!-- don't match tables -->
+        <table>
+            <tr>
+                <th>the-table</th>
+                <td>the-table</td>
+            </tr>
+        </table>
+    </div>
+
+    <input name="the-table"/>
+</body>
+</html>
diff --git a/core/vendor/behat/mink/tests/SessionTest.php b/core/vendor/behat/mink/tests/SessionTest.php
new file mode 100644
index 0000000..b000ba6
--- /dev/null
+++ b/core/vendor/behat/mink/tests/SessionTest.php
@@ -0,0 +1,282 @@
+<?php
+
+namespace Behat\Mink\Tests;
+
+use Behat\Mink\Session;
+
+class SessionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $driver;
+    private $selectorsHandler;
+
+    /**
+     * Session.
+     *
+     * @var Session
+     */
+    private $session;
+
+    protected function setUp()
+    {
+        $this->driver           = $this->getMockBuilder('Behat\Mink\Driver\DriverInterface')->getMock();
+        $this->selectorsHandler = $this->getMockBuilder('Behat\Mink\Selector\SelectorsHandler')->getMock();
+        $this->session  = new Session($this->driver, $this->selectorsHandler);
+    }
+
+    public function testGetDriver()
+    {
+        $this->assertSame($this->driver, $this->session->getDriver());
+    }
+
+    public function testGetPage()
+    {
+        $this->assertInstanceOf('Behat\Mink\Element\DocumentElement', $this->session->getPage());
+    }
+
+    public function testGetSelectorsHandler()
+    {
+        $this->assertSame($this->selectorsHandler, $this->session->getSelectorsHandler());
+    }
+
+    public function testIsStarted()
+    {
+        $this->driver->expects($this->once())
+            ->method('isStarted')
+            ->will($this->returnValue(true));
+
+        $this->assertTrue($this->session->isStarted());
+    }
+
+    public function testStart()
+    {
+        $this->driver->expects($this->once())
+            ->method('start');
+
+        $this->session->start();
+    }
+
+    public function testStop()
+    {
+        $this->driver->expects($this->once())
+            ->method('stop');
+
+        $this->session->stop();
+    }
+
+    public function testRestart()
+    {
+        $this->driver->expects($this->at(0))
+            ->method('stop');
+        $this->driver->expects($this->at(1))
+            ->method('start');
+
+        $this->session->restart();
+    }
+
+    public function testVisit()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('visit')
+            ->with($url = 'some_url');
+
+        $this->session->visit($url);
+    }
+
+    public function testReset()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('reset');
+
+        $this->session->reset();
+    }
+
+    public function testSetBasicAuth()
+    {
+        $this->driver->expects($this->once())
+            ->method('setBasicAuth')
+            ->with('user', 'pass');
+
+        $this->session->setBasicAuth('user', 'pass');
+    }
+
+    public function testSetRequestHeader()
+    {
+        $this->driver->expects($this->once())
+            ->method('setRequestHeader')
+            ->with('name', 'value');
+
+        $this->session->setRequestHeader('name', 'value');
+    }
+
+    public function testGetResponseHeaders()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('getResponseHeaders')
+            ->will($this->returnValue($ret = array(2, 3, 4)));
+
+        $this->assertEquals($ret, $this->session->getResponseHeaders());
+    }
+
+    public function testSetCookie()
+    {
+        $this->driver->expects($this->once())
+            ->method('setCookie')
+            ->with('name', 'value');
+
+        $this->session->setCookie('name', 'value');
+    }
+
+    public function testGetCookie()
+    {
+        $this->driver->expects($this->once())
+            ->method('getCookie')
+            ->with('name')
+            ->will($this->returnValue('value'));
+
+        $this->assertEquals('value', $this->session->getCookie('name'));
+    }
+
+    public function testGetStatusCode()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('getStatusCode')
+            ->will($this->returnValue($ret = 404));
+
+        $this->assertEquals($ret, $this->session->getStatusCode());
+    }
+
+    public function testGetCurrentUrl()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('getCurrentUrl')
+            ->will($this->returnValue($ret = 'http://some.url'));
+
+        $this->assertEquals($ret, $this->session->getCurrentUrl());
+    }
+
+    public function testGetScreenshot()
+    {
+        $this->driver->expects($this->once())
+            ->method('getScreenshot')
+            ->will($this->returnValue('screenshot'));
+
+        $this->assertEquals('screenshot', $this->session->getScreenshot());
+    }
+
+    public function testGetWindowNames()
+    {
+        $this->driver->expects($this->once())
+            ->method('getWindowNames')
+            ->will($this->returnValue($names = array('window 1', 'window 2')));
+
+        $this->assertEquals($names, $this->session->getWindowNames());
+    }
+
+    public function testGetWindowName()
+    {
+        $this->driver->expects($this->once())
+            ->method('getWindowName')
+            ->will($this->returnValue('name'));
+
+        $this->assertEquals('name', $this->session->getWindowName());
+    }
+
+    public function testReload()
+    {
+        $this->driver->expects($this->once())
+            ->method('reload');
+
+        $this->session->reload();
+    }
+
+    public function testBack()
+    {
+        $this->driver->expects($this->once())
+            ->method('back');
+
+        $this->session->back();
+    }
+
+    public function testForward()
+    {
+        $this->driver->expects($this->once())
+            ->method('forward');
+
+        $this->session->forward();
+    }
+
+    public function testSwitchToWindow()
+    {
+        $this->driver->expects($this->once())
+            ->method('switchToWindow')
+            ->with('test');
+
+        $this->session->switchToWindow('test');
+    }
+
+    public function testSwitchToIFrame()
+    {
+        $this->driver->expects($this->once())
+            ->method('switchToIFrame')
+            ->with('test');
+
+        $this->session->switchToIFrame('test');
+    }
+
+    public function testExecuteScript()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('executeScript')
+            ->with($arg = 'JS');
+
+        $this->session->executeScript($arg);
+    }
+
+    public function testEvaluateScript()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('evaluateScript')
+            ->with($arg = 'JS func')
+            ->will($this->returnValue($ret = '23'));
+
+        $this->assertEquals($ret, $this->session->evaluateScript($arg));
+    }
+
+    public function testWait()
+    {
+        $this->driver
+            ->expects($this->once())
+            ->method('wait')
+            ->with(1000, 'function () {}');
+
+        $this->session->wait(1000, 'function () {}');
+    }
+
+    public function testResizeWindow()
+    {
+        $this->driver->expects($this->once())
+            ->method('resizeWindow')
+            ->with(800, 600, 'test');
+
+        $this->session->resizeWindow(800, 600, 'test');
+    }
+
+    public function testMaximizeWindow()
+    {
+        $this->driver->expects($this->once())
+            ->method('maximizeWindow')
+            ->with('test');
+
+        $this->session->maximizeWindow('test');
+    }
+}
diff --git a/core/vendor/behat/mink/tests/WebAssertTest.php b/core/vendor/behat/mink/tests/WebAssertTest.php
new file mode 100644
index 0000000..9ce5d22
--- /dev/null
+++ b/core/vendor/behat/mink/tests/WebAssertTest.php
@@ -0,0 +1,1123 @@
+<?php
+
+namespace Behat\Mink\Tests;
+
+use Behat\Mink\WebAssert;
+
+class WebAssertTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $session;
+    /**
+     * @var WebAssert
+     */
+    private $assert;
+
+    public function setUp()
+    {
+        $this->session = $this->getMockBuilder('Behat\\Mink\\Session')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->assert  = new WebAssert($this->session);
+    }
+
+    public function testAddressEquals()
+    {
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getCurrentUrl')
+            ->will($this->returnValue('http://example.com/script.php/sub/url?param=true#webapp/nav'))
+        ;
+
+        $this->assertCorrectAssertion('addressEquals', array('/sub/url#webapp/nav'));
+        $this->assertWrongAssertion(
+            'addressEquals',
+            array('sub_url'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'Current page is "/sub/url#webapp/nav", but "sub_url" expected.'
+        );
+    }
+
+    public function testAddressNotEquals()
+    {
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getCurrentUrl')
+            ->will($this->returnValue('http://example.com/script.php/sub/url'))
+        ;
+
+        $this->assertCorrectAssertion('addressNotEquals', array('sub_url'));
+        $this->assertWrongAssertion(
+            'addressNotEquals',
+            array('/sub/url'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'Current page is "/sub/url", but should not be.'
+        );
+    }
+
+    public function testAddressMatches()
+    {
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getCurrentUrl')
+            ->will($this->returnValue('http://example.com/script.php/sub/url'))
+        ;
+
+        $this->assertCorrectAssertion('addressMatches', array('/su.*rl/'));
+        $this->assertWrongAssertion(
+            'addressMatches',
+            array('/suburl/'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'Current page "/sub/url" does not match the regex "/suburl/".'
+        );
+    }
+
+    /**
+     * @covers Behat\Mink\WebAssert::cookieEquals
+     */
+    public function testCookieEquals()
+    {
+        $this->session->
+            expects($this->any())->
+            method('getCookie')->
+            will($this->returnValueMap(
+                array(
+                    array('foo', 'bar'),
+                    array('bar', 'baz'),
+                )
+            ));
+
+        $this->assertCorrectAssertion('cookieEquals', array('foo', 'bar'));
+        $this->assertWrongAssertion(
+            'cookieEquals',
+            array('bar', 'foo'),
+            'Behat\Mink\Exception\ExpectationException',
+            'Cookie "bar" value is "baz", but should be "foo".'
+        );
+    }
+
+    /**
+     * @covers Behat\Mink\WebAssert::cookieExists
+     */
+    public function testCookieExists()
+    {
+        $this->session->
+            expects($this->any())->
+            method('getCookie')->
+            will($this->returnValueMap(
+                array(
+                    array('foo', '1'),
+                    array('bar', null),
+                )
+            ));
+
+        $this->assertCorrectAssertion('cookieExists', array('foo'));
+        $this->assertWrongAssertion(
+            'cookieExists',
+            array('bar'),
+            'Behat\Mink\Exception\ExpectationException',
+            'Cookie "bar" is not set, but should be.'
+        );
+    }
+
+    public function testStatusCodeEquals()
+    {
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getStatusCode')
+            ->will($this->returnValue(200))
+        ;
+
+        $this->assertCorrectAssertion('statusCodeEquals', array(200));
+        $this->assertWrongAssertion(
+            'statusCodeEquals',
+            array(404),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'Current response status code is 200, but 404 expected.'
+        );
+    }
+
+    public function testStatusCodeNotEquals()
+    {
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getStatusCode')
+            ->will($this->returnValue(404))
+        ;
+
+        $this->assertCorrectAssertion('statusCodeNotEquals', array(200));
+        $this->assertWrongAssertion(
+            'statusCodeNotEquals',
+            array(404),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'Current response status code is 404, but should not be.'
+        );
+    }
+
+    public function testPageTextContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getText')
+            ->will($this->returnValue("Some  page\n\ttext"))
+        ;
+
+        $this->assertCorrectAssertion('pageTextContains', array('PAGE text'));
+        $this->assertWrongAssertion(
+            'pageTextContains',
+            array('html text'),
+            'Behat\\Mink\\Exception\\ResponseTextException',
+            'The text "html text" was not found anywhere in the text of the current page.'
+        );
+    }
+
+    public function testPageTextNotContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getText')
+            ->will($this->returnValue("Some  html\n\ttext"))
+        ;
+
+        $this->assertCorrectAssertion('pageTextNotContains', array('PAGE text'));
+        $this->assertWrongAssertion(
+            'pageTextNotContains',
+            array('HTML text'),
+            'Behat\\Mink\\Exception\\ResponseTextException',
+            'The text "HTML text" appears in the text of this page, but it should not.'
+        );
+    }
+
+    public function testPageTextMatches()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getText')
+            ->will($this->returnValue('Some page text'))
+        ;
+
+        $this->assertCorrectAssertion('pageTextMatches', array('/PA.E/i'));
+        $this->assertWrongAssertion(
+            'pageTextMatches',
+            array('/html/'),
+            'Behat\\Mink\\Exception\\ResponseTextException',
+            'The pattern /html/ was not found anywhere in the text of the current page.'
+        );
+    }
+
+    public function testPageTextNotMatches()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getText')
+            ->will($this->returnValue('Some html text'))
+        ;
+
+        $this->assertCorrectAssertion('pageTextNotMatches', array('/PA.E/i'));
+        $this->assertWrongAssertion(
+            'pageTextNotMatches',
+            array('/HTML/i'),
+            'Behat\\Mink\\Exception\\ResponseTextException',
+            'The pattern /HTML/i was found in the text of the current page, but it should not.'
+        );
+    }
+
+    public function testResponseContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getContent')
+            ->will($this->returnValue('Some page text'))
+        ;
+
+        $this->assertCorrectAssertion('responseContains', array('PAGE text'));
+        $this->assertWrongAssertion(
+            'responseContains',
+            array('html text'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The string "html text" was not found anywhere in the HTML response of the current page.'
+        );
+    }
+
+    public function testResponseNotContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getContent')
+            ->will($this->returnValue('Some html text'))
+        ;
+
+        $this->assertCorrectAssertion('responseNotContains', array('PAGE text'));
+        $this->assertWrongAssertion(
+            'responseNotContains',
+            array('HTML text'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The string "HTML text" appears in the HTML response of this page, but it should not.'
+        );
+    }
+
+    public function testResponseMatches()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getContent')
+            ->will($this->returnValue('Some page text'))
+        ;
+
+        $this->assertCorrectAssertion('responseMatches', array('/PA.E/i'));
+        $this->assertWrongAssertion(
+            'responseMatches',
+            array('/html/'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The pattern /html/ was not found anywhere in the HTML response of the page.'
+        );
+    }
+
+    public function testResponseNotMatches()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('getContent')
+            ->will($this->returnValue('Some html text'))
+        ;
+
+        $this->assertCorrectAssertion('responseNotMatches', array('/PA.E/i'));
+        $this->assertWrongAssertion(
+            'responseNotMatches',
+            array('/HTML/i'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The pattern /HTML/i was found in the HTML response of the page, but it should not.'
+        );
+    }
+
+    public function testElementsCount()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('findAll')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue(array(1, 2)))
+        ;
+
+        $this->assertCorrectAssertion('elementsCount', array('css', 'h2 > span', 2));
+        $this->assertWrongAssertion(
+            'elementsCount',
+            array('css', 'h2 > span', 3),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            '2 elements matching css "h2 > span" found on the page, but should be 3.'
+        );
+    }
+
+    public function testElementExists()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(4))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->onConsecutiveCalls(1, null, 1, null))
+        ;
+
+        $this->assertCorrectAssertion('elementExists', array('css', 'h2 > span'));
+        $this->assertWrongAssertion(
+            'elementExists',
+            array('css', 'h2 > span'),
+            'Behat\\Mink\\Exception\\ElementNotFoundException',
+            'Element matching css "h2 > span" not found.'
+        );
+
+        $this->assertCorrectAssertion('elementExists', array('css', 'h2 > span', $page));
+        $this->assertWrongAssertion(
+            'elementExists',
+            array('css', 'h2 > span', $page),
+            'Behat\\Mink\\Exception\\ElementNotFoundException',
+            'Element matching css "h2 > span" not found.'
+        );
+    }
+
+    public function testElementExistsWithArrayLocator()
+    {
+        $container = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session->expects($this->never())
+            ->method('getPage')
+        ;
+
+        $container
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('named', array('element', 'Test'))
+            ->will($this->onConsecutiveCalls(1, null))
+        ;
+
+        $this->assertCorrectAssertion('elementExists', array('named', array('element', 'Test'), $container));
+        $this->assertWrongAssertion(
+            'elementExists',
+            array('named', array('element', 'Test'), $container),
+            'Behat\\Mink\\Exception\\ElementNotFoundException',
+            'Element with named "element Test" not found.'
+        );
+    }
+
+    public function testElementNotExists()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(4))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->onConsecutiveCalls(null, 1, null, 1))
+        ;
+
+        $this->assertCorrectAssertion('elementNotExists', array('css', 'h2 > span'));
+        $this->assertWrongAssertion(
+            'elementNotExists',
+            array('css', 'h2 > span'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'An element matching css "h2 > span" appears on this page, but it should not.'
+        );
+
+        $this->assertCorrectAssertion('elementNotExists', array('css', 'h2 > span', $page));
+        $this->assertWrongAssertion(
+            'elementNotExists',
+            array('css', 'h2 > span', $page),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'An element matching css "h2 > span" appears on this page, but it should not.'
+        );
+    }
+
+    /**
+     * @dataProvider getArrayLocatorFormats
+     */
+    public function testElementNotExistsArrayLocator($selector, $locator, $expectedMessage)
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->once())
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->once())
+            ->method('find')
+            ->with($selector, $locator)
+            ->will($this->returnValue(1))
+        ;
+
+        $this->assertWrongAssertion(
+            'elementNotExists',
+            array($selector, $locator),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            $expectedMessage
+        );
+    }
+
+    public function getArrayLocatorFormats()
+    {
+        return array(
+            'named' => array(
+                'named',
+                array('button', 'Test'),
+                'An button matching locator "Test" appears on this page, but it should not.',
+            ),
+            'custom' => array(
+                'custom',
+                array('test', 'foo'),
+                'An element matching custom "test foo" appears on this page, but it should not.',
+            ),
+        );
+    }
+
+    public function testElementTextContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('getText')
+            ->will($this->returnValue('element text'))
+        ;
+
+        $this->assertCorrectAssertion('elementTextContains', array('css', 'h2 > span', 'text'));
+        $this->assertWrongAssertion(
+            'elementTextContains',
+            array('css', 'h2 > span', 'html'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The text "html" was not found in the text of the element matching css "h2 > span".'
+        );
+    }
+
+    public function testElementTextNotContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('getText')
+            ->will($this->returnValue('element text'))
+        ;
+
+        $this->assertCorrectAssertion('elementTextNotContains', array('css', 'h2 > span', 'html'));
+        $this->assertWrongAssertion(
+            'elementTextNotContains',
+            array('css', 'h2 > span', 'text'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The text "text" appears in the text of the element matching css "h2 > span", but it should not.'
+        );
+    }
+
+    public function testElementContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('getHtml')
+            ->will($this->returnValue('element html'))
+        ;
+
+        $this->assertCorrectAssertion('elementContains', array('css', 'h2 > span', 'html'));
+        $this->assertWrongAssertion(
+            'elementContains',
+            array('css', 'h2 > span', 'text'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The string "text" was not found in the HTML of the element matching css "h2 > span".'
+        );
+    }
+
+    public function testElementNotContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('getHtml')
+            ->will($this->returnValue('element html'))
+        ;
+
+        $this->assertCorrectAssertion('elementNotContains', array('css', 'h2 > span', 'text'));
+        $this->assertWrongAssertion(
+            'elementNotContains',
+            array('css', 'h2 > span', 'html'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The string "html" appears in the HTML of the element matching css "h2 > span", but it should not.'
+        );
+    }
+
+    public function testElementAttributeContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('hasAttribute')
+            ->will($this->returnValue(true))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('getAttribute')
+            ->with('name')
+            ->will($this->returnValue('foo'))
+        ;
+
+        $this->assertCorrectAssertion('elementAttributeContains', array('css', 'h2 > span', 'name', 'foo'));
+        $this->assertWrongAssertion(
+            'elementAttributeContains',
+            array('css', 'h2 > span', 'name', 'bar'),
+            'Behat\\Mink\\Exception\\ElementHtmlException',
+            'The text "bar" was not found in the attribute "name" of the element matching css "h2 > span".'
+        );
+    }
+
+    public function testElementAttributeExists()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->at(0))
+            ->method('hasAttribute')
+            ->with('name')
+            ->will($this->returnValue(true))
+        ;
+
+        $element
+            ->expects($this->at(1))
+            ->method('hasAttribute')
+            ->with('name')
+            ->will($this->returnValue(false))
+        ;
+
+        $this->assertCorrectAssertion('elementAttributeExists', array('css', 'h2 > span', 'name'));
+        $this->assertWrongAssertion(
+            'elementAttributeExists',
+            array('css', 'h2 > span', 'name'),
+            'Behat\\Mink\\Exception\\ElementHtmlException',
+            'The attribute "name" was not found in the element matching css "h2 > span".'
+        );
+    }
+
+    public function testElementAttributeNotContains()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('find')
+            ->with('css', 'h2 > span')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('hasAttribute')
+            ->will($this->returnValue(true))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('getAttribute')
+            ->with('name')
+            ->will($this->returnValue('foo'))
+        ;
+
+        $this->assertCorrectAssertion('elementAttributeNotContains', array('css', 'h2 > span', 'name', 'bar'));
+        $this->assertWrongAssertion(
+            'elementAttributeNotContains',
+            array('css', 'h2 > span', 'name', 'foo'),
+            'Behat\\Mink\\Exception\\ElementHtmlException',
+            'The text "foo" was found in the attribute "name" of the element matching css "h2 > span".'
+        );
+    }
+
+    public function testFieldExists()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('findField')
+            ->with('username')
+            ->will($this->onConsecutiveCalls($element, null))
+        ;
+
+        $this->assertCorrectAssertion('fieldExists', array('username'));
+        $this->assertWrongAssertion(
+            'fieldExists',
+            array('username'),
+            'Behat\\Mink\\Exception\\ElementNotFoundException',
+            'Form field with id|name|label|value "username" not found.'
+        );
+    }
+
+    public function testFieldNotExists()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('findField')
+            ->with('username')
+            ->will($this->onConsecutiveCalls(null, $element))
+        ;
+
+        $this->assertCorrectAssertion('fieldNotExists', array('username'));
+        $this->assertWrongAssertion(
+            'fieldNotExists',
+            array('username'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'A field "username" appears on this page, but it should not.'
+        );
+    }
+
+    public function testFieldValueEquals()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(4))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(4))
+            ->method('findField')
+            ->with('username')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(4))
+            ->method('getValue')
+            ->will($this->returnValue(234))
+        ;
+
+        $this->assertCorrectAssertion('fieldValueEquals', array('username', 234));
+        $this->assertWrongAssertion(
+            'fieldValueEquals',
+            array('username', 235),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The field "username" value is "234", but "235" expected.'
+        );
+        $this->assertWrongAssertion(
+            'fieldValueEquals',
+            array('username', 23),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The field "username" value is "234", but "23" expected.'
+        );
+        $this->assertWrongAssertion(
+            'fieldValueEquals',
+            array('username', ''),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The field "username" value is "234", but "" expected.'
+        );
+    }
+
+    public function testFieldValueNotEquals()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(4))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(4))
+            ->method('findField')
+            ->with('username')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(4))
+            ->method('getValue')
+            ->will($this->returnValue(235))
+        ;
+
+        $this->assertCorrectAssertion('fieldValueNotEquals', array('username', 234));
+        $this->assertWrongAssertion(
+            'fieldValueNotEquals',
+            array('username', 235),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'The field "username" value is "235", but it should not be.'
+        );
+        $this->assertCorrectAssertion('fieldValueNotEquals', array('username', 23));
+        $this->assertCorrectAssertion('fieldValueNotEquals', array('username', ''));
+    }
+
+    public function testCheckboxChecked()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('findField')
+            ->with('remember_me')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('isChecked')
+            ->will($this->onConsecutiveCalls(true, false))
+        ;
+
+        $this->assertCorrectAssertion('checkboxChecked', array('remember_me'));
+        $this->assertWrongAssertion(
+            'checkboxChecked',
+            array('remember_me'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'Checkbox "remember_me" is not checked, but it should be.'
+        );
+    }
+
+    public function testCheckboxNotChecked()
+    {
+        $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $this->session
+            ->expects($this->exactly(2))
+            ->method('getPage')
+            ->will($this->returnValue($page))
+        ;
+
+        $page
+            ->expects($this->exactly(2))
+            ->method('findField')
+            ->with('remember_me')
+            ->will($this->returnValue($element))
+        ;
+
+        $element
+            ->expects($this->exactly(2))
+            ->method('isChecked')
+            ->will($this->onConsecutiveCalls(false, true))
+        ;
+
+        $this->assertCorrectAssertion('checkboxNotChecked', array('remember_me'));
+        $this->assertWrongAssertion(
+            'checkboxNotChecked',
+            array('remember_me'),
+            'Behat\\Mink\\Exception\\ExpectationException',
+            'Checkbox "remember_me" is checked, but it should not be.'
+        );
+    }
+
+    protected function assertCorrectAssertion($assertion, $arguments)
+    {
+        try {
+            call_user_func_array(array($this->assert, $assertion), $arguments);
+        } catch (\Exception $e) {
+            $this->fail('Correct assertion should not throw an exception: '.$e->getMessage());
+        }
+    }
+
+    protected function assertWrongAssertion($assertion, $arguments, $exceptionClass, $exceptionMessage)
+    {
+        try {
+            call_user_func_array(array($this->assert, $assertion), $arguments);
+            $this->fail('Wrong assertion should throw an exception');
+        } catch (\Exception $e) {
+            $this->assertInstanceOf($exceptionClass, $e);
+            $this->assertSame($exceptionMessage, $e->getMessage());
+        }
+    }
+}
diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php
index ffe0cd5..30b3d6c 100644
--- a/core/vendor/composer/autoload_namespaces.php
+++ b/core/vendor/composer/autoload_namespaces.php
@@ -20,10 +20,12 @@
     'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
     'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
     'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
+    'Symfony\\Component\\DomCrawler\\' => array($vendorDir . '/symfony/dom-crawler'),
     'Symfony\\Component\\DependencyInjection\\' => array($vendorDir . '/symfony/dependency-injection'),
     'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
     'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
     'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'),
+    'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'),
     'Stack' => array($vendorDir . '/stack/builder/src'),
     'Psr\\Log\\' => array($vendorDir . '/psr/log'),
     'Gliph' => array($vendorDir . '/sdboyer/gliph/src'),
@@ -36,4 +38,6 @@
     'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib'),
     'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib'),
     'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib'),
+    'Behat\\Mink\\Driver' => array($vendorDir . '/behat/mink-browserkit-driver/src', $vendorDir . '/behat/mink-goutte-driver/src'),
+    'Behat\\Mink' => array($vendorDir . '/behat/mink/src'),
 );
diff --git a/core/vendor/composer/autoload_psr4.php b/core/vendor/composer/autoload_psr4.php
index 8b6150b..e13f29f 100644
--- a/core/vendor/composer/autoload_psr4.php
+++ b/core/vendor/composer/autoload_psr4.php
@@ -11,6 +11,7 @@
     'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'),
     'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'),
     'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
+    'Goutte\\' => array($vendorDir . '/fabpot/goutte/Goutte'),
     'Drupal\\Driver\\' => array($baseDir . '/../drivers/lib/Drupal/Driver'),
     'Drupal\\Core\\' => array($baseDir . '/lib/Drupal/Core'),
     'Drupal\\Component\\' => array($baseDir . '/lib/Drupal/Component'),
diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json
index bc37a15..c813508 100644
--- a/core/vendor/composer/installed.json
+++ b/core/vendor/composer/installed.json
@@ -2724,5 +2724,336 @@
             "testing",
             "xunit"
         ]
+    },
+    {
+        "name": "symfony/dom-crawler",
+        "version": "v2.6.3",
+        "version_normalized": "2.6.3.0",
+        "target-dir": "Symfony/Component/DomCrawler",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/DomCrawler.git",
+            "reference": "26a9eb302decd828990e1015afaa11b78b016073"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/26a9eb302decd828990e1015afaa11b78b016073",
+            "reference": "26a9eb302decd828990e1015afaa11b78b016073",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "require-dev": {
+            "symfony/css-selector": "~2.3"
+        },
+        "suggest": {
+            "symfony/css-selector": ""
+        },
+        "time": "2015-01-03 08:01:59",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.6-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\DomCrawler\\": ""
+            }
+        },
+        "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 DomCrawler Component",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "symfony/browser-kit",
+        "version": "v2.6.3",
+        "version_normalized": "2.6.3.0",
+        "target-dir": "Symfony/Component/BrowserKit",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/BrowserKit.git",
+            "reference": "2ecec44ed5047020c65dd6e4a4b2f3cf13ae3c04"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/2ecec44ed5047020c65dd6e4a4b2f3cf13ae3c04",
+            "reference": "2ecec44ed5047020c65dd6e4a4b2f3cf13ae3c04",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3",
+            "symfony/dom-crawler": "~2.0,>=2.0.5"
+        },
+        "require-dev": {
+            "symfony/css-selector": "~2.0,>=2.0.5",
+            "symfony/process": "~2.0,>=2.0.5"
+        },
+        "suggest": {
+            "symfony/process": ""
+        },
+        "time": "2015-01-03 08:01:59",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.6-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\BrowserKit\\": ""
+            }
+        },
+        "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 BrowserKit Component",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "fabpot/goutte",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/FriendsOfPHP/Goutte.git",
+            "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/65ab61eae03d670b93a9044ad2328eb81aa1bde5",
+            "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5",
+            "shasum": ""
+        },
+        "require": {
+            "guzzlehttp/guzzle": ">=4,<6",
+            "php": ">=5.4.0",
+            "symfony/browser-kit": "~2.1",
+            "symfony/css-selector": "~2.1",
+            "symfony/dom-crawler": "~2.1"
+        },
+        "time": "2014-11-28 09:48:17",
+        "type": "application",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.0-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Goutte\\": "Goutte"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            }
+        ],
+        "description": "A simple PHP Web Scraper",
+        "homepage": "https://github.com/FriendsOfPHP/Goutte",
+        "keywords": [
+            "scraper"
+        ]
+    },
+    {
+        "name": "behat/mink",
+        "version": "v1.6.0",
+        "version_normalized": "1.6.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/minkphp/Mink.git",
+            "reference": "090900a0049c441f1e072bbd837db4079b2250c5"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/minkphp/Mink/zipball/090900a0049c441f1e072bbd837db4079b2250c5",
+            "reference": "090900a0049c441f1e072bbd837db4079b2250c5",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.1",
+            "symfony/css-selector": "~2.0"
+        },
+        "suggest": {
+            "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)",
+            "behat/mink-goutte-driver": "fast headless driver for any app without JS emulation",
+            "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)",
+            "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)"
+        },
+        "time": "2014-09-26 09:25:05",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.6.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Behat\\Mink": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Konstantin Kudryashov",
+                "email": "ever.zet@gmail.com",
+                "homepage": "http://everzet.com"
+            }
+        ],
+        "description": "Web acceptance testing framework for PHP 5.3",
+        "homepage": "http://mink.behat.org/",
+        "keywords": [
+            "browser",
+            "testing",
+            "web"
+        ]
+    },
+    {
+        "name": "behat/mink-browserkit-driver",
+        "version": "v1.2.0",
+        "version_normalized": "1.2.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/minkphp/MinkBrowserKitDriver.git",
+            "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/aed8f4a596b79014a75254c3e337511c33e38cbd",
+            "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd",
+            "shasum": ""
+        },
+        "require": {
+            "behat/mink": "~1.6@dev",
+            "php": ">=5.3.1",
+            "symfony/browser-kit": "~2.0",
+            "symfony/dom-crawler": "~2.0"
+        },
+        "require-dev": {
+            "silex/silex": "~1.2"
+        },
+        "time": "2014-09-26 11:35:19",
+        "type": "mink-driver",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.2.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Behat\\Mink\\Driver": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Konstantin Kudryashov",
+                "email": "ever.zet@gmail.com",
+                "homepage": "http://everzet.com"
+            }
+        ],
+        "description": "Symfony2 BrowserKit driver for Mink framework",
+        "homepage": "http://mink.behat.org/",
+        "keywords": [
+            "Mink",
+            "Symfony2",
+            "browser",
+            "testing"
+        ]
+    },
+    {
+        "name": "behat/mink-goutte-driver",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/minkphp/MinkGoutteDriver.git",
+            "reference": "7f531a03233f60b2292352d4eaa1fc45975aba2c"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/7f531a03233f60b2292352d4eaa1fc45975aba2c",
+            "reference": "7f531a03233f60b2292352d4eaa1fc45975aba2c",
+            "shasum": ""
+        },
+        "require": {
+            "behat/mink": "~1.6@dev",
+            "behat/mink-browserkit-driver": "~1.2@dev",
+            "fabpot/goutte": "~1.0.4|~2.0",
+            "php": ">=5.3.1"
+        },
+        "time": "2014-12-19 11:23:33",
+        "type": "mink-driver",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.1.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Behat\\Mink\\Driver": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Konstantin Kudryashov",
+                "email": "ever.zet@gmail.com",
+                "homepage": "http://everzet.com"
+            }
+        ],
+        "description": "Goutte driver for Mink framework",
+        "homepage": "http://mink.behat.org/",
+        "keywords": [
+            "browser",
+            "goutte",
+            "headless",
+            "testing"
+        ]
     }
 ]
diff --git a/core/vendor/fabpot/goutte/.travis.yml b/core/vendor/fabpot/goutte/.travis.yml
new file mode 100644
index 0000000..66cf320
--- /dev/null
+++ b/core/vendor/fabpot/goutte/.travis.yml
@@ -0,0 +1,17 @@
+language: php
+
+php:
+    - 5.6
+    - 5.5
+    - 5.4
+    - hhvm
+
+install:
+    - travis_retry composer install --no-interaction --prefer-source
+
+script:
+    - phpunit
+
+matrix:
+    allow_failures:
+        - php: hhvm
diff --git a/core/vendor/fabpot/goutte/Goutte/Client.php b/core/vendor/fabpot/goutte/Goutte/Client.php
new file mode 100644
index 0000000..cde3ea3
--- /dev/null
+++ b/core/vendor/fabpot/goutte/Goutte/Client.php
@@ -0,0 +1,171 @@
+<?php
+
+/*
+ * This file is part of the Goutte package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Goutte;
+
+use GuzzleHttp\Client as GuzzleClient;
+use GuzzleHttp\ClientInterface as GuzzleClientInterface;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Message\RequestInterface;
+use GuzzleHttp\Message\Response as GuzzleResponse;
+use GuzzleHttp\Post\PostFile;
+use Symfony\Component\BrowserKit\Client as BaseClient;
+use Symfony\Component\BrowserKit\Response;
+
+/**
+ * Client.
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @author Michael Dowling <michael@guzzlephp.org>
+ */
+class Client extends BaseClient
+{
+    protected $client;
+
+    private $headers = array();
+    private $auth = null;
+
+    public function setClient(GuzzleClientInterface $client)
+    {
+        $this->client = $client;
+
+        return $this;
+    }
+
+    public function getClient()
+    {
+        if (!$this->client) {
+            $this->client = new GuzzleClient(array('defaults' => array('allow_redirects' => false, 'cookies' => true)));
+        }
+
+        return $this->client;
+    }
+
+    public function setHeader($name, $value)
+    {
+        $this->headers[$name] = $value;
+
+        return $this;
+    }
+
+    public function removeHeader($name)
+    {
+        unset($this->headers[$name]);
+    }
+
+    public function setAuth($user, $password = '', $type = 'basic')
+    {
+        $this->auth = array($user, $password, $type);
+
+        return $this;
+    }
+
+    public function resetAuth()
+    {
+        $this->auth = null;
+
+        return $this;
+    }
+
+    protected function doRequest($request)
+    {
+        $headers = array();
+        foreach ($request->getServer() as $key => $val) {
+            $key = strtolower(str_replace('_', '-', $key));
+            $contentHeaders = array('content-length' => true, 'content-md5' => true, 'content-type' => true);
+            if (0 === strpos($key, 'http-')) {
+                $headers[substr($key, 5)] = $val;
+            }
+            // CONTENT_* are not prefixed with HTTP_
+            elseif (isset($contentHeaders[$key])) {
+                $headers[$key] = $val;
+            }
+        }
+
+        $body = null;
+        if (!in_array($request->getMethod(), array('GET', 'HEAD'))) {
+            if (null !== $request->getContent()) {
+                $body = $request->getContent();
+            } else {
+                $body = $request->getParameters();
+            }
+        }
+
+        $this->getClient()->setDefaultOption('auth', $this->auth);
+
+        $requestOptions = array(
+            'body' => $body,
+            'cookies' => $this->getCookieJar()->allRawValues($request->getUri()),
+            'allow_redirects' => false,
+            'timeout' => 30,
+        );
+
+        if (!empty($headers)) {
+            $requestOptions['headers'] = $headers;
+        }
+
+        $guzzleRequest = $this->getClient()->createRequest(
+            $request->getMethod(),
+            $request->getUri(),
+            $requestOptions
+        );
+
+        foreach ($this->headers as $name => $value) {
+            $guzzleRequest->setHeader($name, $value);
+        }
+
+        if ('POST' == $request->getMethod() || 'PUT' == $request->getMethod()) {
+            $this->addPostFiles($guzzleRequest, $request->getFiles());
+        }
+
+        // Let BrowserKit handle redirects
+        try {
+            $response = $this->getClient()->send($guzzleRequest);
+        } catch (RequestException $e) {
+            $response = $e->getResponse();
+            if (null === $response) {
+                throw $e;
+            }
+        }
+
+        return $this->createResponse($response);
+    }
+
+    protected function addPostFiles(RequestInterface $request, array $files, $arrayName = '')
+    {
+        foreach ($files as $name => $info) {
+            if (!empty($arrayName)) {
+                $name = $arrayName.'['.$name.']';
+            }
+
+            if (is_array($info)) {
+                if (isset($info['tmp_name'])) {
+                    if ('' !== $info['tmp_name']) {
+                        $request->getBody()->addFile(new PostFile($name, fopen($info['tmp_name'], 'r'), isset($info['name']) ? $info['name'] : null));
+                    } else {
+                        continue;
+                    }
+                } else {
+                    $this->addPostFiles($request, $info, $name);
+                }
+            } else {
+                $request->getBody()->addFile(new PostFile($name, fopen($info, 'r')));
+            }
+        }
+    }
+
+    protected function createResponse(GuzzleResponse $response)
+    {
+        $headers = $response->getHeaders();
+
+        return new Response($response->getBody(true), $response->getStatusCode(), $headers);
+    }
+}
diff --git a/core/vendor/fabpot/goutte/Goutte/Resources/phar-stub.php b/core/vendor/fabpot/goutte/Goutte/Resources/phar-stub.php
new file mode 100644
index 0000000..b9c3b44
--- /dev/null
+++ b/core/vendor/fabpot/goutte/Goutte/Resources/phar-stub.php
@@ -0,0 +1,14 @@
+<?php
+
+/*
+ * This file is part of the Goutte utility.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+require_once 'phar://'.__FILE__.'/vendor/autoload.php';
+
+__HALT_COMPILER();
diff --git a/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php b/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php
new file mode 100644
index 0000000..da2e47c
--- /dev/null
+++ b/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php
@@ -0,0 +1,319 @@
+<?php
+
+/*
+ * This file is part of the Goutte package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Goutte\Tests;
+
+use Goutte\Client;
+use GuzzleHttp\Client as GuzzleClient;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Message\Response as GuzzleResponse;
+use GuzzleHttp\Stream\Stream;
+use GuzzleHttp\Subscriber\History;
+use GuzzleHttp\Subscriber\Mock;
+use GuzzleHttp\Post\PostFile;
+use Symfony\Component\BrowserKit\Cookie;
+
+/**
+ * Goutte Client Test
+ *
+ * @author Michael Dowling <michael@guzzlephp.org>
+ */
+class ClientTest extends \PHPUnit_Framework_TestCase
+{
+    protected $history;
+    protected $mock;
+
+    protected function getGuzzle()
+    {
+        $this->history = new History();
+        $this->mock = new Mock();
+        $this->mock->addResponse(new GuzzleResponse(200, array(), Stream::factory('<html><body><p>Hi</p></body></html>')));
+        $guzzle = new GuzzleClient(array('redirect.disable' => true, 'base_url' => ''));
+        $guzzle->getEmitter()->attach($this->mock);
+        $guzzle->getEmitter()->attach($this->history);
+
+        return $guzzle;
+    }
+
+    public function testCreatesDefaultClient()
+    {
+        $client = new Client();
+        $this->assertInstanceOf('GuzzleHttp\\ClientInterface', $client->getClient());
+    }
+
+    public function testUsesCustomClient()
+    {
+        $guzzle = new GuzzleClient();
+        $client = new Client();
+        $this->assertSame($client, $client->setClient($guzzle));
+        $this->assertSame($guzzle, $client->getClient());
+    }
+
+    public function testUsesCustomHeaders()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $client->setHeader('X-Test', 'test');
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $this->assertEquals('test', $this->history->getLastRequest()->getHeader('X-Test'));
+    }
+
+    public function testCustomUserAgent()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $client->setHeader('User-Agent', 'foo');
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $this->assertEquals('foo', $this->history->getLastRequest()->getHeader('User-Agent'));
+    }
+
+    public function testUsesAuth()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $client->setAuth('me', '**');
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $request = $this->history->getLastRequest();
+        $this->assertEquals('me', $request->getConfig()->get('auth')[0]);
+        $this->assertEquals('**', $request->getConfig()->get('auth')[1]);
+        $this->assertEquals('basic', $request->getConfig()->get('auth')[2]);
+    }
+
+    public function testResetsAuth()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $client->setAuth('me', '**');
+        $client->resetAuth();
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $request = $this->history->getLastRequest();
+        $this->assertNull($request->getConfig()->get('auth')[0]);
+        $this->assertNull($request->getConfig()->get('auth')[1]);
+    }
+
+    public function testUsesCookies()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $client->getCookieJar()->set(new Cookie('test', '123'));
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $request = $this->history->getLastRequest();
+        $this->assertEquals('test=123', $request->getHeader('Cookie'));
+    }
+
+    public function testUsesPostFiles()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $files = array(
+            'test' => array(
+                'name' => 'test.txt',
+                'tmp_name' => __FILE__,
+            ),
+        );
+
+        $crawler = $client->request('POST', 'http://www.example.com/', array(), $files);
+        $request = $this->history->getLastRequest();
+
+        $files = $request->getBody()->getFiles();
+        $this->assertFile(reset($files), 'test', 'test.txt', array(
+          'Content-Type' => 'text/plain',
+          'Content-Disposition' => 'form-data; filename="test.txt"; name="test"',
+        ));
+    }
+
+    public function testUsesPostNamedFiles()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $files = array(
+            'test' => __FILE__,
+        );
+
+        $crawler = $client->request('POST', 'http://www.example.com/', array(), $files);
+        $request = $this->history->getLastRequest();
+        $files = $request->getBody()->getFiles();
+        $this->assertFile(reset($files), 'test', __FILE__, array(
+          'Content-Type' => 'text/x-php',
+          'Content-Disposition' => 'form-data; filename="ClientTest.php"; name="test"',
+        ));
+    }
+
+    public function testUsesPostFilesNestedFields()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $files = array(
+            'form' => array(
+                'test' => array(
+                    'name' => 'test.txt',
+                    'tmp_name' => __FILE__,
+                ),
+            ),
+        );
+
+        $crawler = $client->request('POST', 'http://www.example.com/', array(), $files);
+        $request = $this->history->getLastRequest();
+        $files = $request->getBody()->getFiles();
+        $this->assertFile(reset($files), 'form[test]', 'test.txt', array(
+            'Content-Type' => 'text/plain',
+            'Content-Disposition' => 'form-data; filename="test.txt"; name="form[test]"',
+        ));
+    }
+
+    public function testUsesPostFilesOnClientSide()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $files = array(
+            'test' => __FILE__,
+        );
+
+        $crawler = $client->request('POST', 'http://www.example.com/', array(), $files);
+        $request = $this->history->getLastRequest();
+        $files = $request->getBody()->getFiles();
+        $this->assertFile(reset($files), 'test', __FILE__, array(
+          'Content-Type' => 'text/x-php',
+          'Content-Disposition' => 'form-data; filename="ClientTest.php"; name="test"',
+        ));
+    }
+
+    public function testUsesPostFilesUploadError()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $files = array(
+            'test' => array(
+                'name' => '',
+                'type' => '',
+                'tmp_name' => '',
+                'error' => 4,
+                'size' => 0,
+            ),
+        );
+
+        $crawler = $client->request('POST', 'http://www.example.com/', array(), $files);
+        $request = $this->history->getLastRequest();
+
+        $this->assertEquals(array(), $request->getBody()->getFiles());
+    }
+
+    public function testCreatesResponse()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client();
+        $client->setClient($guzzle);
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $this->assertEquals('Hi', $crawler->filter('p')->text());
+    }
+
+    public function testHandlesRedirectsCorrectly()
+    {
+        $guzzle = $this->getGuzzle();
+
+        $this->mock->clearQueue();
+        $this->mock->addResponse(new GuzzleResponse(301, array(
+            'Location' => 'http://www.example.com/',
+        )));
+        $this->mock->addResponse(new GuzzleResponse(200, [], Stream::factory('<html><body><p>Test</p></body></html>')));
+
+        $client = new Client();
+        $client->setClient($guzzle);
+
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $this->assertEquals('Test', $crawler->filter('p')->text());
+
+        // Ensure that two requests were sent
+        $this->assertEquals(2, count($this->history));
+    }
+
+    public function testConvertsGuzzleHeadersToArrays()
+    {
+        $guzzle = $this->getGuzzle();
+
+        $this->mock->clearQueue();
+        $this->mock->addResponse(new GuzzleResponse(200, array(
+            'Date' => 'Tue, 04 Jun 2013 13:22:41 GMT',
+        )));
+
+        $client = new Client();
+        $client->setClient($guzzle);
+        $client->request('GET', 'http://www.example.com/');
+        $response = $client->getResponse();
+        $headers = $response->getHeaders();
+
+        $this->assertInternalType("array", array_shift($headers), "Header not converted from Guzzle\Http\Message\Header to array");
+    }
+
+    public function testNullResponseException()
+    {
+        $this->setExpectedException('GuzzleHttp\Exception\RequestException');
+        $guzzle = $this->getGuzzle();
+        $this->mock->clearQueue();
+        $exception = new RequestException('', $this->getMock('GuzzleHttp\Message\RequestInterface'));
+        $this->mock->addException($exception);
+        $client = new Client();
+        $client->setClient($guzzle);
+        $client->request('GET', 'http://www.example.com/');
+        $response = $client->getResponse();
+    }
+
+    protected function assertFile(PostFile $postFile, $fieldName, $fileName, $headers)
+    {
+        $this->assertEquals($postFile->getName(), $fieldName);
+        $this->assertEquals($postFile->getFilename(), $fileName);
+
+        $postFileHeaders = $postFile->getHeaders();
+
+        // Note: Sort 'Content-Disposition' values before comparing, because the order changed in Guzzle 4.2.2
+        $postFileHeaders['Content-Disposition'] = explode('; ', $postFileHeaders['Content-Disposition']);
+        sort($postFileHeaders['Content-Disposition']);
+        $headers['Content-Disposition'] = explode('; ', $headers['Content-Disposition']);
+        sort($headers['Content-Disposition']);
+
+        $this->assertEquals($postFileHeaders, $headers);
+    }
+
+    public function testHttps()
+    {
+        $guzzle = $this->getGuzzle();
+
+        $this->mock->clearQueue();
+        $this->mock->addResponse(new GuzzleResponse(200, [], Stream::factory('<html><body><p>Test</p></body></html>')));
+        $client = new Client();
+        $client->setClient($guzzle);
+        $crawler = $client->request('GET', 'https://www.example.com/');
+        $this->assertEquals('https', $this->history->getLastRequest()->getScheme());
+        $this->assertEquals('Test', $crawler->filter('p')->text());
+    }
+
+    public function testCustomUserAgentConstructor()
+    {
+        $guzzle = $this->getGuzzle();
+        $client = new Client([
+          'HTTP_HOST' => '1.2.3.4',
+          'HTTP_USER_AGENT' => 'SomeHost',
+        ]);
+        $client->setClient($guzzle);
+        $crawler = $client->request('GET', 'http://www.example.com/');
+        $this->assertEquals('SomeHost', $this->history->getLastRequest()->getHeader('User-Agent'));
+    }
+}
diff --git a/core/vendor/fabpot/goutte/LICENSE b/core/vendor/fabpot/goutte/LICENSE
new file mode 100644
index 0000000..ff727f4
--- /dev/null
+++ b/core/vendor/fabpot/goutte/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2013 Fabien Potencier
+
+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/core/vendor/fabpot/goutte/README.rst b/core/vendor/fabpot/goutte/README.rst
new file mode 100644
index 0000000..58eecdb
--- /dev/null
+++ b/core/vendor/fabpot/goutte/README.rst
@@ -0,0 +1,114 @@
+Goutte, a simple PHP Web Scraper
+================================
+
+Goutte is a screen scraping and web crawling library for PHP.
+
+Goutte provides a nice API to crawl websites and extract data from the HTML/XML
+responses.
+
+Requirements
+------------
+
+Goutte depends on PHP 5.4+ and Guzzle 4+.
+
+.. tip::
+
+    If you need support for PHP 5.3 or Guzzle 3, use Goutte 1.0.6.
+
+Installation
+------------
+
+Add ``fabpot/goutte`` as a require dependency in your ``composer.json`` file:
+
+.. code-block:: bash
+
+    php composer.phar require fabpot/goutte:~2.0
+
+.. tip::
+
+    You can also download the `Goutte.phar`_ file:
+
+    .. code-block:: php
+
+        require_once '/path/to/goutte.phar';
+
+Usage
+-----
+
+Create a Goutte Client instance (which extends
+``Symfony\Component\BrowserKit\Client``):
+
+.. code-block:: php
+
+    use Goutte\Client;
+
+    $client = new Client();
+
+Make requests with the ``request()`` method:
+
+.. code-block:: php
+
+    // Go to the symfony.com website
+    $crawler = $client->request('GET', 'http://www.symfony.com/blog/');
+
+The method returns a ``Crawler`` object
+(``Symfony\Component\DomCrawler\Crawler``).
+
+Fine-tune cURL options:
+
+.. code-block:: php
+
+    $client->getClient()->setDefaultOption('config/curl/'.CURLOPT_TIMEOUT, 60);
+
+Click on links:
+
+.. code-block:: php
+
+    // Click on the "Security Advisories" link
+    $link = $crawler->selectLink('Security Advisories')->link();
+    $crawler = $client->click($link);
+
+Extract data:
+
+.. code-block:: php
+
+    // Get the latest post in this category and display the titles
+    $crawler->filter('h2.post > a')->each(function ($node) {
+        print $node->text()."\n";
+    });
+
+Submit forms:
+
+.. code-block:: php
+
+    $crawler = $client->request('GET', 'http://github.com/');
+    $crawler = $client->click($crawler->selectLink('Sign in')->link());
+    $form = $crawler->selectButton('Sign in')->form();
+    $crawler = $client->submit($form, array('login' => 'fabpot', 'password' => 'xxxxxx'));
+    $crawler->filter('.flash-error')->each(function ($node) {
+        print $node->text()."\n";
+    });
+
+More Information
+----------------
+
+Read the documentation of the BrowserKit and DomCrawler Symfony Components for
+more information about what you can do with Goutte.
+
+Technical Information
+---------------------
+
+Goutte is a thin wrapper around the following fine PHP libraries:
+
+* Symfony Components: BrowserKit, CssSelector and DomCrawler;
+
+*  `Guzzle`_ HTTP Component.
+
+License
+-------
+
+Goutte is licensed under the MIT license.
+
+.. _`Composer`:    http://getcomposer.org
+.. _`Goutte.phar`: http://get.sensiolabs.org/goutte.phar
+.. _`Guzzle`:      http://docs.guzzlephp.org
diff --git a/core/vendor/fabpot/goutte/box.json b/core/vendor/fabpot/goutte/box.json
new file mode 100644
index 0000000..943c61e
--- /dev/null
+++ b/core/vendor/fabpot/goutte/box.json
@@ -0,0 +1,21 @@
+{
+    "output": "goutte.phar",
+    "chmod": "0755",
+    "compactors": [
+        "Herrera\\Box\\Compactor\\Php"
+    ],
+    "extract": false,
+    "files": [
+        "LICENSE",
+        "Goutte/Client.php"
+    ],
+    "finder": [
+        {
+            "name": ["*.php", "*.pem*"],
+            "exclude": ["Tests", "tests"],
+            "in": "vendor"
+        }
+    ],
+    "stub": "Goutte/Resources/phar-stub.php",
+    "web": false
+}
diff --git a/core/vendor/fabpot/goutte/composer.json b/core/vendor/fabpot/goutte/composer.json
new file mode 100644
index 0000000..63d87e7
--- /dev/null
+++ b/core/vendor/fabpot/goutte/composer.json
@@ -0,0 +1,29 @@
+{
+    "name": "fabpot/goutte",
+    "type": "application",
+    "description": "A simple PHP Web Scraper",
+    "keywords": ["scraper"],
+    "homepage": "https://github.com/FriendsOfPHP/Goutte",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.4.0",
+        "symfony/browser-kit": "~2.1",
+        "symfony/css-selector": "~2.1",
+        "symfony/dom-crawler": "~2.1",
+        "guzzlehttp/guzzle": ">=4,<6"
+    },
+    "autoload": {
+        "psr-4": { "Goutte\\": "Goutte" }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.0-dev"
+        }
+    }
+}
diff --git a/core/vendor/fabpot/goutte/phpunit.xml.dist b/core/vendor/fabpot/goutte/phpunit.xml.dist
new file mode 100644
index 0000000..9e7ff98
--- /dev/null
+++ b/core/vendor/fabpot/goutte/phpunit.xml.dist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="./vendor/autoload.php">
+    <testsuites>
+        <testsuite name="Goutte Test Suite">
+            <directory>./Goutte/Tests</directory>
+        </testsuite>
+    </testsuites>
+</phpunit>
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md
new file mode 100644
index 0000000..d2b1074
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md
@@ -0,0 +1,18 @@
+CHANGELOG
+=========
+
+2.3.0
+-----
+
+ * [BC BREAK] `Client::followRedirect()` won't redirect responses with
+   a non-3xx Status Code and `Location` header anymore, as per 
+   http://tools.ietf.org/html/rfc2616#section-14.30
+
+ * added `Client::getInternalRequest()` and `Client::getInternalResponse()` to
+   have access to the BrowserKit internal request and response objects
+
+2.1.0
+-----
+
+ * [BC BREAK] The CookieJar internals have changed to allow cookies with the
+   same name on different sub-domains/sub-paths
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php
new file mode 100644
index 0000000..90df108
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php
@@ -0,0 +1,619 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+use Symfony\Component\DomCrawler\Crawler;
+use Symfony\Component\DomCrawler\Link;
+use Symfony\Component\DomCrawler\Form;
+use Symfony\Component\Process\PhpProcess;
+
+/**
+ * Client simulates a browser.
+ *
+ * To make the actual request, you need to implement the doRequest() method.
+ *
+ * If you want to be able to run requests in their own process (insulated flag),
+ * you need to also implement the getScript() method.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+abstract class Client
+{
+    protected $history;
+    protected $cookieJar;
+    protected $server = array();
+    protected $internalRequest;
+    protected $request;
+    protected $internalResponse;
+    protected $response;
+    protected $crawler;
+    protected $insulated = false;
+    protected $redirect;
+    protected $followRedirects = true;
+
+    private $maxRedirects = -1;
+    private $redirectCount = 0;
+    private $isMainRequest = true;
+
+    /**
+     * Constructor.
+     *
+     * @param array     $server    The server parameters (equivalent of $_SERVER)
+     * @param History   $history   A History instance to store the browser history
+     * @param CookieJar $cookieJar A CookieJar instance to store the cookies
+     *
+     * @api
+     */
+    public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null)
+    {
+        $this->setServerParameters($server);
+        $this->history = $history ?: new History();
+        $this->cookieJar = $cookieJar ?: new CookieJar();
+    }
+
+    /**
+     * Sets whether to automatically follow redirects or not.
+     *
+     * @param bool $followRedirect Whether to follow redirects
+     *
+     * @api
+     */
+    public function followRedirects($followRedirect = true)
+    {
+        $this->followRedirects = (bool) $followRedirect;
+    }
+
+    /**
+     * Sets the maximum number of requests that crawler can follow.
+     *
+     * @param int $maxRedirects
+     */
+    public function setMaxRedirects($maxRedirects)
+    {
+        $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects;
+        $this->followRedirects = -1 != $this->maxRedirects;
+    }
+
+    /**
+     * Sets the insulated flag.
+     *
+     * @param bool $insulated Whether to insulate the requests or not
+     *
+     * @throws \RuntimeException When Symfony Process Component is not installed
+     *
+     * @api
+     */
+    public function insulate($insulated = true)
+    {
+        if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
+            throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.');
+        }
+
+        $this->insulated = (bool) $insulated;
+    }
+
+    /**
+     * Sets server parameters.
+     *
+     * @param array $server An array of server parameters
+     *
+     * @api
+     */
+    public function setServerParameters(array $server)
+    {
+        $this->server = array_merge(array(
+            'HTTP_HOST' => 'localhost',
+            'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
+        ), $server);
+    }
+
+    /**
+     * Sets single server parameter.
+     *
+     * @param string $key   A key of the parameter
+     * @param string $value A value of the parameter
+     */
+    public function setServerParameter($key, $value)
+    {
+        $this->server[$key] = $value;
+    }
+
+    /**
+     * Gets single server parameter for specified key.
+     *
+     * @param string $key     A key of the parameter to get
+     * @param string $default A default value when key is undefined
+     *
+     * @return string A value of the parameter
+     */
+    public function getServerParameter($key, $default = '')
+    {
+        return (isset($this->server[$key])) ? $this->server[$key] : $default;
+    }
+
+    /**
+     * Returns the History instance.
+     *
+     * @return History A History instance
+     *
+     * @api
+     */
+    public function getHistory()
+    {
+        return $this->history;
+    }
+
+    /**
+     * Returns the CookieJar instance.
+     *
+     * @return CookieJar A CookieJar instance
+     *
+     * @api
+     */
+    public function getCookieJar()
+    {
+        return $this->cookieJar;
+    }
+
+    /**
+     * Returns the current Crawler instance.
+     *
+     * @return Crawler|null A Crawler instance
+     *
+     * @api
+     */
+    public function getCrawler()
+    {
+        return $this->crawler;
+    }
+
+    /**
+     * Returns the current BrowserKit Response instance.
+     *
+     * @return Response|null A BrowserKit Response instance
+     *
+     * @api
+     */
+    public function getInternalResponse()
+    {
+        return $this->internalResponse;
+    }
+
+    /**
+     * Returns the current origin response instance.
+     *
+     * The origin response is the response instance that is returned
+     * by the code that handles requests.
+     *
+     * @return object|null A response instance
+     *
+     * @see doRequest()
+     *
+     * @api
+     */
+    public function getResponse()
+    {
+        return $this->response;
+    }
+
+    /**
+     * Returns the current BrowserKit Request instance.
+     *
+     * @return Request|null A BrowserKit Request instance
+     *
+     * @api
+     */
+    public function getInternalRequest()
+    {
+        return $this->internalRequest;
+    }
+
+    /**
+     * Returns the current origin Request instance.
+     *
+     * The origin request is the request instance that is sent
+     * to the code that handles requests.
+     *
+     * @return object|null A Request instance
+     *
+     * @see doRequest()
+     *
+     * @api
+     */
+    public function getRequest()
+    {
+        return $this->request;
+    }
+
+    /**
+     * Clicks on a given link.
+     *
+     * @param Link $link A Link instance
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function click(Link $link)
+    {
+        if ($link instanceof Form) {
+            return $this->submit($link);
+        }
+
+        return $this->request($link->getMethod(), $link->getUri());
+    }
+
+    /**
+     * Submits a form.
+     *
+     * @param Form  $form   A Form instance
+     * @param array $values An array of form field values
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function submit(Form $form, array $values = array())
+    {
+        $form->setValues($values);
+
+        return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
+    }
+
+    /**
+     * Calls a URI.
+     *
+     * @param string $method        The request method
+     * @param string $uri           The URI to fetch
+     * @param array  $parameters    The Request parameters
+     * @param array  $files         The files
+     * @param array  $server        The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does)
+     * @param string $content       The raw body data
+     * @param bool   $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)
+    {
+        if ($this->isMainRequest) {
+            $this->redirectCount = 0;
+        } else {
+            ++$this->redirectCount;
+        }
+
+        $uri = $this->getAbsoluteUri($uri);
+
+        if (!empty($server['HTTP_HOST'])) {
+            $uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri);
+        }
+
+        if (isset($server['HTTPS'])) {
+            $uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri);
+        }
+
+        $server = array_merge($this->server, $server);
+
+        if (!$this->history->isEmpty()) {
+            $server['HTTP_REFERER'] = $this->history->current()->getUri();
+        }
+
+        $server['HTTP_HOST'] = $this->extractHost($uri);
+        $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME);
+
+        $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);
+
+        $this->request = $this->filterRequest($this->internalRequest);
+
+        if (true === $changeHistory) {
+            $this->history->add($this->internalRequest);
+        }
+
+        if ($this->insulated) {
+            $this->response = $this->doRequestInProcess($this->request);
+        } else {
+            $this->response = $this->doRequest($this->request);
+        }
+
+        $this->internalResponse = $this->filterResponse($this->response);
+
+        $this->cookieJar->updateFromResponse($this->internalResponse, $uri);
+
+        $status = $this->internalResponse->getStatus();
+
+        if ($status >= 300 && $status < 400) {
+            $this->redirect = $this->internalResponse->getHeader('Location');
+        } else {
+            $this->redirect = null;
+        }
+
+        if ($this->followRedirects && $this->redirect) {
+            return $this->crawler = $this->followRedirect();
+        }
+
+        return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type'));
+    }
+
+    /**
+     * Makes a request in another process.
+     *
+     * @param object $request An origin request instance
+     *
+     * @return object An origin response instance
+     *
+     * @throws \RuntimeException When processing returns exit code
+     */
+    protected function doRequestInProcess($request)
+    {
+        // We set the TMPDIR (for Macs) and TEMP (for Windows), because on these platforms the temp directory changes based on the user.
+        $process = new PhpProcess($this->getScript($request), null, array('TMPDIR' => sys_get_temp_dir(), 'TEMP' => sys_get_temp_dir()));
+        $process->run();
+
+        if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
+            throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s', $process->getOutput(), $process->getErrorOutput()));
+        }
+
+        return unserialize($process->getOutput());
+    }
+
+    /**
+     * Makes a request.
+     *
+     * @param object $request An origin request instance
+     *
+     * @return object An origin response instance
+     */
+    abstract protected function doRequest($request);
+
+    /**
+     * Returns the script to execute when the request must be insulated.
+     *
+     * @param object $request An origin request instance
+     *
+     * @throws \LogicException When this abstract class is not implemented
+     */
+    protected function getScript($request)
+    {
+        throw new \LogicException('To insulate requests, you need to override the getScript() method.');
+    }
+
+    /**
+     * Filters the BrowserKit request to the origin one.
+     *
+     * @param Request $request The BrowserKit Request to filter
+     *
+     * @return object An origin request instance
+     */
+    protected function filterRequest(Request $request)
+    {
+        return $request;
+    }
+
+    /**
+     * Filters the origin response to the BrowserKit one.
+     *
+     * @param object $response The origin response to filter
+     *
+     * @return Response An BrowserKit Response instance
+     */
+    protected function filterResponse($response)
+    {
+        return $response;
+    }
+
+    /**
+     * Creates a crawler.
+     *
+     * This method returns null if the DomCrawler component is not available.
+     *
+     * @param string $uri     A URI
+     * @param string $content Content for the crawler to use
+     * @param string $type    Content type
+     *
+     * @return Crawler|null
+     */
+    protected function createCrawlerFromContent($uri, $content, $type)
+    {
+        if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
+            return;
+        }
+
+        $crawler = new Crawler(null, $uri);
+        $crawler->addContent($content, $type);
+
+        return $crawler;
+    }
+
+    /**
+     * Goes back in the browser history.
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function back()
+    {
+        return $this->requestFromRequest($this->history->back(), false);
+    }
+
+    /**
+     * Goes forward in the browser history.
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function forward()
+    {
+        return $this->requestFromRequest($this->history->forward(), false);
+    }
+
+    /**
+     * Reloads the current browser.
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function reload()
+    {
+        return $this->requestFromRequest($this->history->current(), false);
+    }
+
+    /**
+     * Follow redirects?
+     *
+     * @return Crawler
+     *
+     * @throws \LogicException If request was not a redirect
+     *
+     * @api
+     */
+    public function followRedirect()
+    {
+        if (empty($this->redirect)) {
+            throw new \LogicException('The request was not redirected.');
+        }
+
+        if (-1 !== $this->maxRedirects) {
+            if ($this->redirectCount > $this->maxRedirects) {
+                throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects));
+            }
+        }
+
+        $request = $this->internalRequest;
+
+        if (in_array($this->internalResponse->getStatus(), array(302, 303))) {
+            $method = 'get';
+            $files = array();
+            $content = null;
+        } else {
+            $method = $request->getMethod();
+            $files = $request->getFiles();
+            $content = $request->getContent();
+        }
+
+        if ('get' === strtolower($method)) {
+            // Don't forward parameters for GET request as it should reach the redirection URI
+            $parameters = array();
+        } else {
+            $parameters = $request->getParameters();
+        }
+
+        $server = $request->getServer();
+        $server = $this->updateServerFromUri($server, $this->redirect);
+
+        $this->isMainRequest = false;
+
+        $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content);
+
+        $this->isMainRequest = true;
+
+        return $response;
+    }
+
+    /**
+     * Restarts the client.
+     *
+     * It flushes history and all cookies.
+     *
+     * @api
+     */
+    public function restart()
+    {
+        $this->cookieJar->clear();
+        $this->history->clear();
+    }
+
+    /**
+     * Takes a URI and converts it to absolute if it is not already absolute.
+     *
+     * @param string $uri A URI
+     *
+     * @return string An absolute URI
+     */
+    protected function getAbsoluteUri($uri)
+    {
+        // already absolute?
+        if (0 === strpos($uri, 'http')) {
+            return $uri;
+        }
+
+        if (!$this->history->isEmpty()) {
+            $currentUri = $this->history->current()->getUri();
+        } else {
+            $currentUri = sprintf('http%s://%s/',
+                isset($this->server['HTTPS']) ? 's' : '',
+                isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost'
+            );
+        }
+
+        // protocol relative URL
+        if (0 === strpos($uri, '//')) {
+            return parse_url($currentUri, PHP_URL_SCHEME).':'.$uri;
+        }
+
+        // anchor?
+        if (!$uri || '#' == $uri[0]) {
+            return preg_replace('/#.*?$/', '', $currentUri).$uri;
+        }
+
+        if ('/' !== $uri[0]) {
+            $path = parse_url($currentUri, PHP_URL_PATH);
+
+            if ('/' !== substr($path, -1)) {
+                $path = substr($path, 0, strrpos($path, '/') + 1);
+            }
+
+            $uri = $path.$uri;
+        }
+
+        return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri;
+    }
+
+    /**
+     * Makes a request from a Request object directly.
+     *
+     * @param Request $request       A Request instance
+     * @param bool    $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
+     *
+     * @return Crawler
+     */
+    protected function requestFromRequest(Request $request, $changeHistory = true)
+    {
+        return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
+    }
+
+    private function updateServerFromUri($server, $uri)
+    {
+        $server['HTTP_HOST'] = $this->extractHost($uri);
+        $scheme = parse_url($uri, PHP_URL_SCHEME);
+        $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme;
+        unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']);
+
+        return $server;
+    }
+
+    private function extractHost($uri)
+    {
+        $host = parse_url($uri, PHP_URL_HOST);
+
+        if ($port = parse_url($uri, PHP_URL_PORT)) {
+            return $host.':'.$port;
+        }
+
+        return $host;
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php
new file mode 100644
index 0000000..e937a62
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php
@@ -0,0 +1,332 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * Cookie represents an HTTP cookie.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Cookie
+{
+    /**
+     * Handles dates as defined by RFC 2616 section 3.3.1, and also some other
+     * non-standard, but common formats.
+     *
+     * @var array
+     */
+    private static $dateFormats = array(
+        'D, d M Y H:i:s T',
+        'D, d-M-y H:i:s T',
+        'D, d-M-Y H:i:s T',
+        'D, d-m-y H:i:s T',
+        'D, d-m-Y H:i:s T',
+        'D M j G:i:s Y',
+        'D M d H:i:s Y T',
+    );
+
+    protected $name;
+    protected $value;
+    protected $expires;
+    protected $path;
+    protected $domain;
+    protected $secure;
+    protected $httponly;
+    protected $rawValue;
+
+    /**
+     * Sets a cookie.
+     *
+     * @param string $name         The cookie name
+     * @param string $value        The value of the cookie
+     * @param string $expires      The time the cookie expires
+     * @param string $path         The path on the server in which the cookie will be available on
+     * @param string $domain       The domain that the cookie is available
+     * @param bool   $secure       Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client
+     * @param bool   $httponly     The cookie httponly flag
+     * @param bool   $encodedValue Whether the value is encoded or not
+     *
+     * @api
+     */
+    public function __construct($name, $value, $expires = null, $path = null, $domain = '', $secure = false, $httponly = true, $encodedValue = false)
+    {
+        if ($encodedValue) {
+            $this->value = urldecode($value);
+            $this->rawValue = $value;
+        } else {
+            $this->value = $value;
+            $this->rawValue = urlencode($value);
+        }
+        $this->name = $name;
+        $this->expires = null === $expires ? null : (int) $expires;
+        $this->path = empty($path) ? '/' : $path;
+        $this->domain = $domain;
+        $this->secure = (bool) $secure;
+        $this->httponly = (bool) $httponly;
+    }
+
+    /**
+     * Returns the HTTP representation of the Cookie.
+     *
+     * @return string The HTTP representation of the Cookie
+     *
+     * @throws \UnexpectedValueException
+     *
+     * @api
+     */
+    public function __toString()
+    {
+        $cookie = sprintf('%s=%s', $this->name, $this->rawValue);
+
+        if (null !== $this->expires) {
+            $dateTime = \DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT'));
+
+            if ($dateTime === false) {
+                throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.'), $this->expires);
+            }
+
+            $cookie .= '; expires='.str_replace('+0000', '', $dateTime->format(self::$dateFormats[0]));
+        }
+
+        if ('' !== $this->domain) {
+            $cookie .= '; domain='.$this->domain;
+        }
+
+        if ($this->path) {
+            $cookie .= '; path='.$this->path;
+        }
+
+        if ($this->secure) {
+            $cookie .= '; secure';
+        }
+
+        if ($this->httponly) {
+            $cookie .= '; httponly';
+        }
+
+        return $cookie;
+    }
+
+    /**
+     * Creates a Cookie instance from a Set-Cookie header value.
+     *
+     * @param string $cookie A Set-Cookie header value
+     * @param string $url    The base URL
+     *
+     * @return Cookie A Cookie instance
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @api
+     */
+    public static function fromString($cookie, $url = null)
+    {
+        $parts = explode(';', $cookie);
+
+        if (false === strpos($parts[0], '=')) {
+            throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0]));
+        }
+
+        list($name, $value) = explode('=', array_shift($parts), 2);
+
+        $values = array(
+            'name' => trim($name),
+            'value' => trim($value),
+            'expires' => null,
+            'path' => '/',
+            'domain' => '',
+            'secure' => false,
+            'httponly' => false,
+            'passedRawValue' => true,
+        );
+
+        if (null !== $url) {
+            if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host'])) {
+                throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url));
+            }
+
+            $values['domain'] = $urlParts['host'];
+            $values['path'] = isset($urlParts['path']) ? substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) : '';
+        }
+
+        foreach ($parts as $part) {
+            $part = trim($part);
+
+            if ('secure' === strtolower($part)) {
+                // Ignore the secure flag if the original URI is not given or is not HTTPS
+                if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) {
+                    continue;
+                }
+
+                $values['secure'] = true;
+
+                continue;
+            }
+
+            if ('httponly' === strtolower($part)) {
+                $values['httponly'] = true;
+
+                continue;
+            }
+
+            if (2 === count($elements = explode('=', $part, 2))) {
+                if ('expires' === strtolower($elements[0])) {
+                    $elements[1] = self::parseDate($elements[1]);
+                }
+
+                $values[strtolower($elements[0])] = $elements[1];
+            }
+        }
+
+        return new static(
+            $values['name'],
+            $values['value'],
+            $values['expires'],
+            $values['path'],
+            $values['domain'],
+            $values['secure'],
+            $values['httponly'],
+            $values['passedRawValue']
+        );
+    }
+
+    private static function parseDate($dateValue)
+    {
+        // trim single quotes around date if present
+        if (($length = strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length-1]) {
+            $dateValue = substr($dateValue, 1, -1);
+        }
+
+        foreach (self::$dateFormats as $dateFormat) {
+            if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) {
+                return $date->getTimestamp();
+            }
+        }
+
+        // attempt a fallback for unusual formatting
+        if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) {
+            return $date->getTimestamp();
+        }
+
+        throw new \InvalidArgumentException(sprintf('Could not parse date "%s".', $dateValue));
+    }
+
+    /**
+     * Gets the name of the cookie.
+     *
+     * @return string The cookie name
+     *
+     * @api
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the value of the cookie.
+     *
+     * @return string The cookie value
+     *
+     * @api
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Gets the raw value of the cookie.
+     *
+     * @return string The cookie value
+     *
+     * @api
+     */
+    public function getRawValue()
+    {
+        return $this->rawValue;
+    }
+
+    /**
+     * Gets the expires time of the cookie.
+     *
+     * @return string The cookie expires time
+     *
+     * @api
+     */
+    public function getExpiresTime()
+    {
+        return $this->expires;
+    }
+
+    /**
+     * Gets the path of the cookie.
+     *
+     * @return string The cookie path
+     *
+     * @api
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Gets the domain of the cookie.
+     *
+     * @return string The cookie domain
+     *
+     * @api
+     */
+    public function getDomain()
+    {
+        return $this->domain;
+    }
+
+    /**
+     * Returns the secure flag of the cookie.
+     *
+     * @return bool The cookie secure flag
+     *
+     * @api
+     */
+    public function isSecure()
+    {
+        return $this->secure;
+    }
+
+    /**
+     * Returns the httponly flag of the cookie.
+     *
+     * @return bool The cookie httponly flag
+     *
+     * @api
+     */
+    public function isHttpOnly()
+    {
+        return $this->httponly;
+    }
+
+    /**
+     * Returns true if the cookie has expired.
+     *
+     * @return bool true if the cookie has expired, false otherwise
+     *
+     * @api
+     */
+    public function isExpired()
+    {
+        return null !== $this->expires && 0 !== $this->expires && $this->expires < time();
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php
new file mode 100644
index 0000000..1a36818
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php
@@ -0,0 +1,265 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * CookieJar.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class CookieJar
+{
+    protected $cookieJar = array();
+
+    /**
+     * Sets a cookie.
+     *
+     * @param Cookie $cookie A Cookie instance
+     *
+     * @api
+     */
+    public function set(Cookie $cookie)
+    {
+        $this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
+    }
+
+    /**
+     * Gets a cookie by name.
+     *
+     * You should never use an empty domain, but if you do so,
+     * this method returns the first cookie for the given name/path
+     * (this behavior ensures a BC behavior with previous versions of
+     * Symfony).
+     *
+     * @param string $name   The cookie name
+     * @param string $path   The cookie path
+     * @param string $domain The cookie domain
+     *
+     * @return Cookie|null A Cookie instance or null if the cookie does not exist
+     *
+     * @api
+     */
+    public function get($name, $path = '/', $domain = null)
+    {
+        $this->flushExpiredCookies();
+
+        if (!empty($domain)) {
+            foreach ($this->cookieJar as $cookieDomain => $pathCookies) {
+                if ($cookieDomain) {
+                    $cookieDomain = '.'.ltrim($cookieDomain, '.');
+                    if ($cookieDomain != substr('.'.$domain, -strlen($cookieDomain))) {
+                        continue;
+                    }
+                }
+
+                foreach ($pathCookies as $cookiePath => $namedCookies) {
+                    if ($cookiePath != substr($path, 0, strlen($cookiePath))) {
+                        continue;
+                    }
+                    if (isset($namedCookies[$name])) {
+                        return $namedCookies[$name];
+                    }
+                }
+            }
+
+            return;
+        }
+
+        // avoid relying on this behavior that is mainly here for BC reasons
+        foreach ($this->cookieJar as $cookies) {
+            if (isset($cookies[$path][$name])) {
+                return $cookies[$path][$name];
+            }
+        }
+    }
+
+    /**
+     * Removes a cookie by name.
+     *
+     * You should never use an empty domain, but if you do so,
+     * all cookies for the given name/path expire (this behavior
+     * ensures a BC behavior with previous versions of Symfony).
+     *
+     * @param string $name   The cookie name
+     * @param string $path   The cookie path
+     * @param string $domain The cookie domain
+     *
+     * @api
+     */
+    public function expire($name, $path = '/', $domain = null)
+    {
+        if (null === $path) {
+            $path = '/';
+        }
+
+        if (empty($domain)) {
+            // an empty domain means any domain
+            // this should never happen but it allows for a better BC
+            $domains = array_keys($this->cookieJar);
+        } else {
+            $domains = array($domain);
+        }
+
+        foreach ($domains as $domain) {
+            unset($this->cookieJar[$domain][$path][$name]);
+
+            if (empty($this->cookieJar[$domain][$path])) {
+                unset($this->cookieJar[$domain][$path]);
+
+                if (empty($this->cookieJar[$domain])) {
+                    unset($this->cookieJar[$domain]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes all the cookies from the jar.
+     *
+     * @api
+     */
+    public function clear()
+    {
+        $this->cookieJar = array();
+    }
+
+    /**
+     * Updates the cookie jar from a response Set-Cookie headers.
+     *
+     * @param array  $setCookies Set-Cookie headers from an HTTP response
+     * @param string $uri        The base URL
+     */
+    public function updateFromSetCookie(array $setCookies, $uri = null)
+    {
+        $cookies = array();
+
+        foreach ($setCookies as $cookie) {
+            foreach (explode(',', $cookie) as $i => $part) {
+                if (0 === $i || preg_match('/^(?P<token>\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) {
+                    $cookies[] = ltrim($part);
+                } else {
+                    $cookies[count($cookies) - 1] .= ','.$part;
+                }
+            }
+        }
+
+        foreach ($cookies as $cookie) {
+            try {
+                $this->set(Cookie::fromString($cookie, $uri));
+            } catch (\InvalidArgumentException $e) {
+                // invalid cookies are just ignored
+            }
+        }
+    }
+
+    /**
+     * Updates the cookie jar from a Response object.
+     *
+     * @param Response $response A Response object
+     * @param string   $uri      The base URL
+     */
+    public function updateFromResponse(Response $response, $uri = null)
+    {
+        $this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri);
+    }
+
+    /**
+     * Returns not yet expired cookies.
+     *
+     * @return Cookie[] An array of cookies
+     */
+    public function all()
+    {
+        $this->flushExpiredCookies();
+
+        $flattenedCookies = array();
+        foreach ($this->cookieJar as $path) {
+            foreach ($path as $cookies) {
+                foreach ($cookies as $cookie) {
+                    $flattenedCookies[] = $cookie;
+                }
+            }
+        }
+
+        return $flattenedCookies;
+    }
+
+    /**
+     * Returns not yet expired cookie values for the given URI.
+     *
+     * @param string $uri             A URI
+     * @param bool   $returnsRawValue Returns raw value or urldecoded value
+     *
+     * @return array An array of cookie values
+     */
+    public function allValues($uri, $returnsRawValue = false)
+    {
+        $this->flushExpiredCookies();
+
+        $parts = array_replace(array('path' => '/'), parse_url($uri));
+        $cookies = array();
+        foreach ($this->cookieJar as $domain => $pathCookies) {
+            if ($domain) {
+                $domain = '.'.ltrim($domain, '.');
+                if ($domain != substr('.'.$parts['host'], -strlen($domain))) {
+                    continue;
+                }
+            }
+
+            foreach ($pathCookies as $path => $namedCookies) {
+                if ($path != substr($parts['path'], 0, strlen($path))) {
+                    continue;
+                }
+
+                foreach ($namedCookies as $cookie) {
+                    if ($cookie->isSecure() && 'https' != $parts['scheme']) {
+                        continue;
+                    }
+
+                    $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue();
+                }
+            }
+        }
+
+        return $cookies;
+    }
+
+    /**
+     * Returns not yet expired raw cookie values for the given URI.
+     *
+     * @param string $uri A URI
+     *
+     * @return array An array of cookie values
+     */
+    public function allRawValues($uri)
+    {
+        return $this->allValues($uri, true);
+    }
+
+    /**
+     * Removes all expired cookies.
+     */
+    public function flushExpiredCookies()
+    {
+        foreach ($this->cookieJar as $domain => $pathCookies) {
+            foreach ($pathCookies as $path => $namedCookies) {
+                foreach ($namedCookies as $name => $cookie) {
+                    if ($cookie->isExpired()) {
+                        unset($this->cookieJar[$domain][$path][$name]);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php
new file mode 100644
index 0000000..8e38fe5
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php
@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * History.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class History
+{
+    protected $stack = array();
+    protected $position = -1;
+
+    /**
+     * Clears the history.
+     */
+    public function clear()
+    {
+        $this->stack = array();
+        $this->position = -1;
+    }
+
+    /**
+     * Adds a Request to the history.
+     *
+     * @param Request $request A Request instance
+     */
+    public function add(Request $request)
+    {
+        $this->stack = array_slice($this->stack, 0, $this->position + 1);
+        $this->stack[] = clone $request;
+        $this->position = count($this->stack) - 1;
+    }
+
+    /**
+     * Returns true if the history is empty.
+     *
+     * @return bool true if the history is empty, false otherwise
+     */
+    public function isEmpty()
+    {
+        return count($this->stack) == 0;
+    }
+
+    /**
+     * Goes back in the history.
+     *
+     * @return Request A Request instance
+     *
+     * @throws \LogicException if the stack is already on the first page
+     */
+    public function back()
+    {
+        if ($this->position < 1) {
+            throw new \LogicException('You are already on the first page.');
+        }
+
+        return clone $this->stack[--$this->position];
+    }
+
+    /**
+     * Goes forward in the history.
+     *
+     * @return Request A Request instance
+     *
+     * @throws \LogicException if the stack is already on the last page
+     */
+    public function forward()
+    {
+        if ($this->position > count($this->stack) - 2) {
+            throw new \LogicException('You are already on the last page.');
+        }
+
+        return clone $this->stack[++$this->position];
+    }
+
+    /**
+     * Returns the current element in the history.
+     *
+     * @return Request A Request instance
+     *
+     * @throws \LogicException if the stack is empty
+     */
+    public function current()
+    {
+        if (-1 == $this->position) {
+            throw new \LogicException('The page history is empty.');
+        }
+
+        return clone $this->stack[$this->position];
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE
new file mode 100644
index 0000000..43028bc
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2015 Fabien Potencier
+
+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/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md
new file mode 100644
index 0000000..1b40c9f
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md
@@ -0,0 +1,23 @@
+BrowserKit Component
+====================
+
+BrowserKit simulates the behavior of a web browser.
+
+The component only provide an abstract client and does not provide any
+"default" backend for the HTTP layer.
+
+Resources
+---------
+
+For a simple implementation of a browser based on an HTTP layer, have a look
+at [Goutte](https://github.com/fabpot/Goutte).
+
+For an implementation based on HttpKernelInterface, have a look at the
+[Client](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Client.php)
+provided by the HttpKernel component.
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/BrowserKit/
+    $ composer.phar install
+    $ phpunit
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php
new file mode 100644
index 0000000..6d381d2
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php
@@ -0,0 +1,138 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * Request object.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Request
+{
+    protected $uri;
+    protected $method;
+    protected $parameters;
+    protected $files;
+    protected $cookies;
+    protected $server;
+    protected $content;
+
+    /**
+     * Constructor.
+     *
+     * @param string $uri        The request URI
+     * @param string $method     The HTTP method request
+     * @param array  $parameters The request parameters
+     * @param array  $files      An array of uploaded files
+     * @param array  $cookies    An array of cookies
+     * @param array  $server     An array of server parameters
+     * @param string $content    The raw body data
+     *
+     * @api
+     */
+    public function __construct($uri, $method, array $parameters = array(), array $files = array(), array $cookies = array(), array $server = array(), $content = null)
+    {
+        $this->uri = $uri;
+        $this->method = $method;
+        $this->parameters = $parameters;
+        $this->files = $files;
+        $this->cookies = $cookies;
+        $this->server = $server;
+        $this->content = $content;
+    }
+
+    /**
+     * Gets the request URI.
+     *
+     * @return string The request URI
+     *
+     * @api
+     */
+    public function getUri()
+    {
+        return $this->uri;
+    }
+
+    /**
+     * Gets the request HTTP method.
+     *
+     * @return string The request HTTP method
+     *
+     * @api
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * Gets the request parameters.
+     *
+     * @return array The request parameters
+     *
+     * @api
+     */
+    public function getParameters()
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Gets the request server files.
+     *
+     * @return array The request files
+     *
+     * @api
+     */
+    public function getFiles()
+    {
+        return $this->files;
+    }
+
+    /**
+     * Gets the request cookies.
+     *
+     * @return array The request cookies
+     *
+     * @api
+     */
+    public function getCookies()
+    {
+        return $this->cookies;
+    }
+
+    /**
+     * Gets the request server parameters.
+     *
+     * @return array The request server parameters
+     *
+     * @api
+     */
+    public function getServer()
+    {
+        return $this->server;
+    }
+
+    /**
+     * Gets the request raw body data.
+     *
+     * @return string The request raw body data.
+     *
+     * @api
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php
new file mode 100644
index 0000000..d00836f
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php
@@ -0,0 +1,138 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * Response object.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Response
+{
+    protected $content;
+    protected $status;
+    protected $headers;
+
+    /**
+     * Constructor.
+     *
+     * The headers array is a set of key/value pairs. If a header is present multiple times
+     * then the value is an array of all the values.
+     *
+     * @param string $content The content of the response
+     * @param int    $status  The response status code
+     * @param array  $headers An array of headers
+     *
+     * @api
+     */
+    public function __construct($content = '', $status = 200, array $headers = array())
+    {
+        $this->content = $content;
+        $this->status = $status;
+        $this->headers = $headers;
+    }
+
+    /**
+     * Converts the response object to string containing all headers and the response content.
+     *
+     * @return string The response with headers and content
+     */
+    public function __toString()
+    {
+        $headers = '';
+        foreach ($this->headers as $name => $value) {
+            if (is_string($value)) {
+                $headers .= $this->buildHeader($name, $value);
+            } else {
+                foreach ($value as $headerValue) {
+                    $headers .= $this->buildHeader($name, $headerValue);
+                }
+            }
+        }
+
+        return $headers."\n".$this->content;
+    }
+
+    /**
+     * Returns the build header line.
+     *
+     * @param string $name  The header name
+     * @param string $value The header value
+     *
+     * @return string The built header line
+     */
+    protected function buildHeader($name, $value)
+    {
+        return sprintf("%s: %s\n", $name, $value);
+    }
+
+    /**
+     * Gets the response content.
+     *
+     * @return string The response content
+     *
+     * @api
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * Gets the response status code.
+     *
+     * @return int The response status code
+     *
+     * @api
+     */
+    public function getStatus()
+    {
+        return $this->status;
+    }
+
+    /**
+     * Gets the response headers.
+     *
+     * @return array The response headers
+     *
+     * @api
+     */
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+
+    /**
+     * Gets a response header.
+     *
+     * @param string $header The header name
+     * @param bool   $first  Whether to return the first value or all header values
+     *
+     * @return string|array The first header value if $first is true, an array of values otherwise
+     */
+    public function getHeader($header, $first = true)
+    {
+        foreach ($this->headers as $key => $value) {
+            if (str_replace('-', '_', strtolower($key)) == str_replace('-', '_', strtolower($header))) {
+                if ($first) {
+                    return is_array($value) ? (count($value) ? $value[0] : '') : $value;
+                }
+
+                return is_array($value) ? $value : array($value);
+            }
+        }
+
+        return $first ? null : array();
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php
new file mode 100644
index 0000000..a6e5ba9
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php
@@ -0,0 +1,624 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Client;
+use Symfony\Component\BrowserKit\History;
+use Symfony\Component\BrowserKit\CookieJar;
+use Symfony\Component\BrowserKit\Response;
+
+class SpecialResponse extends Response
+{
+}
+
+class TestClient extends Client
+{
+    protected $nextResponse = null;
+    protected $nextScript = null;
+
+    public function setNextResponse(Response $response)
+    {
+        $this->nextResponse = $response;
+    }
+
+    public function setNextScript($script)
+    {
+        $this->nextScript = $script;
+    }
+
+    protected function doRequest($request)
+    {
+        if (null === $this->nextResponse) {
+            return new Response();
+        }
+
+        $response = $this->nextResponse;
+        $this->nextResponse = null;
+
+        return $response;
+    }
+
+    protected function filterResponse($response)
+    {
+        if ($response instanceof SpecialResponse) {
+            return new Response($response->getContent(), $response->getStatus(), $response->getHeaders());
+        }
+
+        return $response;
+    }
+
+    protected function getScript($request)
+    {
+        $r = new \ReflectionClass('Symfony\Component\BrowserKit\Response');
+        $path = $r->getFileName();
+
+        return <<<EOF
+<?php
+
+require_once('$path');
+
+echo serialize($this->nextScript);
+EOF;
+    }
+}
+
+class ClientTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getHistory
+     */
+    public function testGetHistory()
+    {
+        $client = new TestClient(array(), $history = new History());
+        $this->assertSame($history, $client->getHistory(), '->getHistory() returns the History');
+    }
+
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getCookieJar
+     */
+    public function testGetCookieJar()
+    {
+        $client = new TestClient(array(), null, $cookieJar = new CookieJar());
+        $this->assertSame($cookieJar, $client->getCookieJar(), '->getCookieJar() returns the CookieJar');
+    }
+
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getRequest
+     */
+    public function testGetRequest()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://example.com/');
+
+        $this->assertEquals('http://example.com/', $client->getRequest()->getUri(), '->getCrawler() returns the Request of the last request');
+    }
+
+    public function testGetRequestWithIpAsHost()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'https://example.com/foo', array(), array(), array('HTTP_HOST' => '127.0.0.1'));
+
+        $this->assertEquals('https://127.0.0.1/foo', $client->getRequest()->getUri());
+    }
+
+    public function testGetResponse()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('foo'));
+        $client->request('GET', 'http://example.com/');
+
+        $this->assertEquals('foo', $client->getResponse()->getContent(), '->getCrawler() returns the Response of the last request');
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getResponse(), '->getCrawler() returns the Response of the last request');
+    }
+
+    public function testGetInternalResponse()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new SpecialResponse('foo'));
+        $client->request('GET', 'http://example.com/');
+
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getInternalResponse());
+        $this->assertNotInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialResponse', $client->getInternalResponse());
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialResponse', $client->getResponse());
+    }
+
+    public function testGetContent()
+    {
+        $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}';
+
+        $client = new TestClient();
+        $client->request('POST', 'http://example.com/jsonrpc', array(), array(), array(), $json);
+        $this->assertEquals($json, $client->getRequest()->getContent());
+    }
+
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getCrawler
+     */
+    public function testGetCrawler()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('foo'));
+        $crawler = $client->request('GET', 'http://example.com/');
+
+        $this->assertSame($crawler, $client->getCrawler(), '->getCrawler() returns the Crawler of the last request');
+    }
+
+    public function testRequestHttpHeaders()
+    {
+        $client = new TestClient();
+        $client->request('GET', '/');
+        $headers = $client->getRequest()->getServer();
+        $this->assertEquals('localhost', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com');
+        $headers = $client->getRequest()->getServer();
+        $this->assertEquals('www.example.com', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header');
+
+        $client->request('GET', 'https://www.example.com');
+        $headers = $client->getRequest()->getServer();
+        $this->assertTrue($headers['HTTPS'], '->request() sets the HTTPS header');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com:8080');
+        $headers = $client->getRequest()->getServer();
+        $this->assertEquals('www.example.com:8080', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header with port');
+    }
+
+    public function testRequestURIConversion()
+    {
+        $client = new TestClient();
+        $client->request('GET', '/foo');
+        $this->assertEquals('http://localhost/foo', $client->getRequest()->getUri(), '->request() converts the URI to an absolute one');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com');
+        $this->assertEquals('http://www.example.com', $client->getRequest()->getUri(), '->request() does not change absolute URIs');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/');
+        $client->request('GET', '/foo');
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo');
+        $client->request('GET', '#');
+        $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #');
+        $client->request('GET', '#');
+        $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #');
+        $client->request('GET', '#foo');
+        $this->assertEquals('http://www.example.com/foo#foo', $client->getRequest()->getUri(), '->request() uses the previous request for #');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/');
+        $client->request('GET', 'bar');
+        $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'bar');
+        $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs');
+    }
+
+    public function testRequestURIConversionByServerHost()
+    {
+        $client = new TestClient();
+
+        $server = array('HTTP_HOST' => 'www.exampl+e.com:8000');
+        $parameters = array();
+        $files = array();
+
+        $client->request('GET', 'http://exampl+e.com', $parameters, $files, $server);
+        $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to add port');
+
+        $client->request('GET', 'http://exampl+e.com:8888', $parameters, $files, $server);
+        $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to modify existing port');
+
+        $client->request('GET', 'http://exampl+e.com:8000', $parameters, $files, $server);
+        $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST respects correct set port');
+    }
+
+    public function testRequestReferer()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'bar');
+        $server = $client->getRequest()->getServer();
+        $this->assertEquals('http://www.example.com/foo/foobar', $server['HTTP_REFERER'], '->request() sets the referer');
+    }
+
+    public function testRequestHistory()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'bar');
+
+        $this->assertEquals('http://www.example.com/foo/bar', $client->getHistory()->current()->getUri(), '->request() updates the History');
+        $this->assertEquals('http://www.example.com/foo/foobar', $client->getHistory()->back()->getUri(), '->request() updates the History');
+    }
+
+    public function testRequestCookies()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><a href="/foo">foo</a></html>', 200, array('Set-Cookie' => 'foo=bar')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar');
+
+        $client->request('GET', 'bar');
+        $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar');
+    }
+
+    public function testRequestSecureCookies()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><a href="/foo">foo</a></html>', 200, array('Set-Cookie' => 'foo=bar; path=/; secure')));
+        $client->request('GET', 'https://www.example.com/foo/foobar');
+
+        $this->assertTrue($client->getCookieJar()->get('foo', '/', 'www.example.com')->isSecure());
+    }
+
+    public function testClick()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><a href="/foo">foo</a></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $client->click($crawler->filter('a')->link());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() clicks on links');
+    }
+
+    public function testClickForm()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><form action="/foo"><input type="submit" /></form></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $client->click($crawler->filter('input')->form());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() Form submit forms');
+    }
+
+    public function testSubmit()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><form action="/foo"><input type="submit" /></form></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $client->submit($crawler->filter('input')->form());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms');
+    }
+
+    public function testSubmitPreserveAuth()
+    {
+        $client = new TestClient(array('PHP_AUTH_USER' => 'foo', 'PHP_AUTH_PW' => 'bar'));
+        $client->setNextResponse(new Response('<html><form action="/foo"><input type="submit" /></form></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $server = $client->getRequest()->getServer();
+        $this->assertArrayHasKey('PHP_AUTH_USER', $server);
+        $this->assertEquals('foo', $server['PHP_AUTH_USER']);
+        $this->assertArrayHasKey('PHP_AUTH_PW', $server);
+        $this->assertEquals('bar', $server['PHP_AUTH_PW']);
+
+        $client->submit($crawler->filter('input')->form());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms');
+
+        $server = $client->getRequest()->getServer();
+        $this->assertArrayHasKey('PHP_AUTH_USER', $server);
+        $this->assertEquals('foo', $server['PHP_AUTH_USER']);
+        $this->assertArrayHasKey('PHP_AUTH_PW', $server);
+        $this->assertEquals('bar', $server['PHP_AUTH_PW']);
+    }
+
+    public function testFollowRedirect()
+    {
+        $client = new TestClient();
+        $client->followRedirects(false);
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        try {
+            $client->followRedirect();
+            $this->fail('->followRedirect() throws a \LogicException if the request was not redirected');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->followRedirect() throws a \LogicException if the request was not redirected');
+        }
+
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->followRedirect();
+
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any');
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() automatically follows redirects if followRedirects is true');
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 201, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->followRedirect() does not follow redirect if HTTP Code is not 30x');
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 201, array('Location' => 'http://www.example.com/redirected')));
+        $client->followRedirects(false);
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        try {
+            $client->followRedirect();
+            $this->fail('->followRedirect() throws a \LogicException if the request did not respond with 30x HTTP Code');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->followRedirect() throws a \LogicException if the request did not respond with 30x HTTP Code');
+        }
+    }
+
+    public function testFollowRelativeRedirect()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 302, array('Location' => '/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any');
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 302, array('Location' => '/redirected:1234')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $this->assertEquals('http://www.example.com/redirected:1234', $client->getRequest()->getUri(), '->followRedirect() follows relative urls');
+    }
+
+    public function testFollowRedirectWithMaxRedirects()
+    {
+        $client = new TestClient();
+        $client->setMaxRedirects(1);
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any');
+
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected2')));
+        try {
+            $client->followRedirect();
+            $this->fail('->followRedirect() throws a \LogicException if the request was redirected and limit of redirections was reached');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->followRedirect() throws a \LogicException if the request was redirected and limit of redirections was reached');
+        }
+
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any');
+
+        $client->setNextResponse(new Response('', 302, array('Location' => '/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows relative URLs');
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 302, array('Location' => '//www.example.org/')));
+        $client->request('GET', 'https://www.example.com/');
+
+        $this->assertEquals('https://www.example.org/', $client->getRequest()->getUri(), '->followRedirect() follows protocol-relative URLs');
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('POST', 'http://www.example.com/foo/foobar', array('name' => 'bar'));
+
+        $this->assertEquals('get', $client->getRequest()->getMethod(), '->followRedirect() uses a get for 302');
+        $this->assertEquals(array(), $client->getRequest()->getParameters(), '->followRedirect() does not submit parameters when changing the method');
+    }
+
+    public function testFollowRedirectWithCookies()
+    {
+        $client = new TestClient();
+        $client->followRedirects(false);
+        $client->setNextResponse(new Response('', 302, array(
+            'Location' => 'http://www.example.com/redirected',
+            'Set-Cookie' => 'foo=bar',
+        )));
+        $client->request('GET', 'http://www.example.com/');
+        $this->assertEquals(array(), $client->getRequest()->getCookies());
+        $client->followRedirect();
+        $this->assertEquals(array('foo' => 'bar'), $client->getRequest()->getCookies());
+    }
+
+    public function testFollowRedirectWithHeaders()
+    {
+        $headers = array(
+            'HTTP_HOST' => 'www.example.com',
+            'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
+            'CONTENT_TYPE' => 'application/vnd.custom+xml',
+            'HTTPS' => false,
+        );
+
+        $client = new TestClient();
+        $client->followRedirects(false);
+        $client->setNextResponse(new Response('', 302, array(
+            'Location' => 'http://www.example.com/redirected',
+        )));
+        $client->request('GET', 'http://www.example.com/', array(), array(), array(
+            'CONTENT_TYPE' => 'application/vnd.custom+xml',
+        ));
+
+        $this->assertEquals($headers, $client->getRequest()->getServer());
+
+        $client->followRedirect();
+
+        $headers['HTTP_REFERER'] = 'http://www.example.com/';
+
+        $this->assertEquals($headers, $client->getRequest()->getServer());
+    }
+
+    public function testFollowRedirectWithPort()
+    {
+        $headers = array(
+            'HTTP_HOST' => 'www.example.com:8080',
+            'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
+            'HTTPS' => false,
+            'HTTP_REFERER' => 'http://www.example.com:8080/',
+        );
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 302, array(
+            'Location' => 'http://www.example.com:8080/redirected',
+        )));
+        $client->request('GET', 'http://www.example.com:8080/');
+
+        $this->assertEquals($headers, $client->getRequest()->getServer());
+    }
+
+    public function testBack()
+    {
+        $client = new TestClient();
+
+        $parameters = array('foo' => 'bar');
+        $files = array('myfile.foo' => 'baz');
+        $server = array('X_TEST_FOO' => 'bazbar');
+        $content = 'foobarbaz';
+
+        $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content);
+        $client->request('GET', 'http://www.example.com/foo');
+        $client->back();
+
+        $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->back() goes back in the history');
+        $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->back() keeps parameters');
+        $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->back() keeps files');
+        $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->back() keeps $_SERVER');
+        $this->assertEquals($content, $client->getRequest()->getContent(), '->back() keeps content');
+    }
+
+    public function testForward()
+    {
+        $client = new TestClient();
+
+        $parameters = array('foo' => 'bar');
+        $files = array('myfile.foo' => 'baz');
+        $server = array('X_TEST_FOO' => 'bazbar');
+        $content = 'foobarbaz';
+
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'http://www.example.com/foo', $parameters, $files, $server, $content);
+        $client->back();
+        $client->forward();
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->forward() goes forward in the history');
+        $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->forward() keeps parameters');
+        $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->forward() keeps files');
+        $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->forward() keeps $_SERVER');
+        $this->assertEquals($content, $client->getRequest()->getContent(), '->forward() keeps content');
+    }
+
+    public function testReload()
+    {
+        $client = new TestClient();
+
+        $parameters = array('foo' => 'bar');
+        $files = array('myfile.foo' => 'baz');
+        $server = array('X_TEST_FOO' => 'bazbar');
+        $content = 'foobarbaz';
+
+        $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content);
+        $client->reload();
+
+        $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->reload() reloads the current page');
+        $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->reload() keeps parameters');
+        $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->reload() keeps files');
+        $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->reload() keeps $_SERVER');
+        $this->assertEquals($content, $client->getRequest()->getContent(), '->reload() keeps content');
+    }
+
+    public function testRestart()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->restart();
+
+        $this->assertTrue($client->getHistory()->isEmpty(), '->restart() clears the history');
+        $this->assertEquals(array(), $client->getCookieJar()->all(), '->restart() clears the cookies');
+    }
+
+    public function testInsulatedRequests()
+    {
+        $client = new TestClient();
+        $client->insulate();
+        $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar')");
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $this->assertEquals('foobar', $client->getResponse()->getContent(), '->insulate() process the request in a forked process');
+
+        $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar)");
+
+        try {
+            $client->request('GET', 'http://www.example.com/foo/foobar');
+            $this->fail('->request() throws a \RuntimeException if the script has an error');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('RuntimeException', $e, '->request() throws a \RuntimeException if the script has an error');
+        }
+    }
+
+    public function testGetServerParameter()
+    {
+        $client = new TestClient();
+        $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST'));
+        $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT'));
+        $this->assertEquals('testvalue', $client->getServerParameter('testkey', 'testvalue'));
+    }
+
+    public function testSetServerParameter()
+    {
+        $client = new TestClient();
+
+        $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST'));
+        $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT'));
+
+        $client->setServerParameter('HTTP_HOST', 'testhost');
+        $this->assertEquals('testhost', $client->getServerParameter('HTTP_HOST'));
+
+        $client->setServerParameter('HTTP_USER_AGENT', 'testua');
+        $this->assertEquals('testua', $client->getServerParameter('HTTP_USER_AGENT'));
+    }
+
+    public function testSetServerParameterInRequest()
+    {
+        $client = new TestClient();
+
+        $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST'));
+        $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT'));
+
+        $client->request('GET', 'https://www.example.com/https/www.example.com', array(), array(), array(
+            'HTTP_HOST' => 'testhost',
+            'HTTP_USER_AGENT' => 'testua',
+            'HTTPS' => false,
+            'NEW_SERVER_KEY' => 'new-server-key-value',
+        ));
+
+        $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST'));
+        $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT'));
+
+        $this->assertEquals('http://testhost/https/www.example.com', $client->getRequest()->getUri());
+
+        $server = $client->getRequest()->getServer();
+
+        $this->assertArrayHasKey('HTTP_USER_AGENT', $server);
+        $this->assertEquals('testua', $server['HTTP_USER_AGENT']);
+
+        $this->assertArrayHasKey('HTTP_HOST', $server);
+        $this->assertEquals('testhost', $server['HTTP_HOST']);
+
+        $this->assertArrayHasKey('NEW_SERVER_KEY', $server);
+        $this->assertEquals('new-server-key-value', $server['NEW_SERVER_KEY']);
+
+        $this->assertArrayHasKey('HTTPS', $server);
+        $this->assertFalse($server['HTTPS']);
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php
new file mode 100644
index 0000000..4da4404
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php
@@ -0,0 +1,231 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\CookieJar;
+use Symfony\Component\BrowserKit\Cookie;
+use Symfony\Component\BrowserKit\Response;
+
+class CookieJarTest extends \PHPUnit_Framework_TestCase
+{
+    public function testSetGet()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie = new Cookie('foo', 'bar'));
+
+        $this->assertEquals($cookie, $cookieJar->get('foo'), '->set() sets a cookie');
+
+        $this->assertNull($cookieJar->get('foobar'), '->get() returns null if the cookie does not exist');
+
+        $cookieJar->set($cookie = new Cookie('foo', 'bar', time() - 86400));
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+    }
+
+    public function testExpire()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie = new Cookie('foo', 'bar'));
+        $cookieJar->expire('foo');
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+    }
+
+    public function testAll()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar'));
+        $cookieJar->set($cookie2 = new Cookie('bar', 'foo'));
+
+        $this->assertEquals(array($cookie1, $cookie2), $cookieJar->all(), '->all() returns all cookies in the jar');
+    }
+
+    public function testClear()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar'));
+        $cookieJar->set($cookie2 = new Cookie('bar', 'foo'));
+
+        $cookieJar->clear();
+
+        $this->assertEquals(array(), $cookieJar->all(), '->clear() expires all cookies');
+    }
+
+    public function testUpdateFromResponse()
+    {
+        $response = new Response('', 200, array('Set-Cookie' => 'foo=foo'));
+
+        $cookieJar = new CookieJar();
+        $cookieJar->updateFromResponse($response);
+
+        $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromResponse() updates cookies from a Response objects');
+    }
+
+    public function testUpdateFromSetCookie()
+    {
+        $setCookies = array('foo=foo');
+
+        $cookieJar = new CookieJar();
+        $cookieJar->set(new Cookie('bar', 'bar'));
+        $cookieJar->updateFromSetCookie($setCookies);
+
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('foo'));
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('bar'));
+        $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromSetCookie() updates cookies from a Set-Cookie header');
+        $this->assertEquals('bar', $cookieJar->get('bar')->getValue(), '->updateFromSetCookie() keeps existing cookies');
+    }
+
+    public function testUpdateFromEmptySetCookie()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->updateFromSetCookie(array(''));
+        $this->assertEquals(array(), $cookieJar->all());
+    }
+
+    public function testUpdateFromSetCookieWithMultipleCookies()
+    {
+        $timestamp = time() + 3600;
+        $date = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
+        $setCookies = array(sprintf('foo=foo; expires=%s; domain=.symfony.com; path=/, bar=bar; domain=.blog.symfony.com, PHPSESSID=id; expires=%s', $date, $date));
+
+        $cookieJar = new CookieJar();
+        $cookieJar->updateFromSetCookie($setCookies);
+
+        $fooCookie = $cookieJar->get('foo', '/', '.symfony.com');
+        $barCookie = $cookieJar->get('bar', '/', '.blog.symfony.com');
+        $phpCookie = $cookieJar->get('PHPSESSID');
+
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $fooCookie);
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $barCookie);
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $phpCookie);
+        $this->assertEquals('foo', $fooCookie->getValue());
+        $this->assertEquals('bar', $barCookie->getValue());
+        $this->assertEquals('id', $phpCookie->getValue());
+        $this->assertEquals($timestamp, $fooCookie->getExpiresTime());
+        $this->assertNull($barCookie->getExpiresTime());
+        $this->assertEquals($timestamp, $phpCookie->getExpiresTime());
+    }
+
+    /**
+     * @dataProvider provideAllValuesValues
+     */
+    public function testAllValues($uri, $values)
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo_nothing', 'foo'));
+        $cookieJar->set($cookie2 = new Cookie('foo_expired', 'foo', time() - 86400));
+        $cookieJar->set($cookie3 = new Cookie('foo_path', 'foo', null, '/foo'));
+        $cookieJar->set($cookie4 = new Cookie('foo_domain', 'foo', null, '/', '.example.com'));
+        $cookieJar->set($cookie4 = new Cookie('foo_strict_domain', 'foo', null, '/', '.www4.example.com'));
+        $cookieJar->set($cookie5 = new Cookie('foo_secure', 'foo', null, '/', '', true));
+
+        $this->assertEquals($values, array_keys($cookieJar->allValues($uri)), '->allValues() returns the cookie for a given URI');
+    }
+
+    public function provideAllValuesValues()
+    {
+        return array(
+            array('http://www.example.com', array('foo_nothing', 'foo_domain')),
+            array('http://www.example.com/', array('foo_nothing', 'foo_domain')),
+            array('http://foo.example.com/', array('foo_nothing', 'foo_domain')),
+            array('http://foo.example1.com/', array('foo_nothing')),
+            array('https://foo.example.com/', array('foo_nothing', 'foo_secure', 'foo_domain')),
+            array('http://www.example.com/foo/bar', array('foo_nothing', 'foo_path', 'foo_domain')),
+            array('http://www4.example.com/', array('foo_nothing', 'foo_domain', 'foo_strict_domain')),
+        );
+    }
+
+    public function testEncodedValues()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true));
+
+        $this->assertEquals(array('foo' => 'bar=baz'), $cookieJar->allValues('/'));
+        $this->assertEquals(array('foo' => 'bar%3Dbaz'), $cookieJar->allRawValues('/'));
+    }
+
+    public function testCookieExpireWithSameNameButDifferentPaths()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo'));
+        $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar'));
+        $cookieJar->expire('foo', '/foo');
+
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+        $this->assertEquals(array(), $cookieJar->allValues('http://example.com/foo'));
+        $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar'));
+    }
+
+    public function testCookieExpireWithNullPaths()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/'));
+        $cookieJar->expire('foo', null);
+
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+    }
+
+    public function testCookieWithSameNameButDifferentPaths()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo'));
+        $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar'));
+
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+        $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://example.com/foo'));
+        $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar'));
+    }
+
+    public function testCookieWithSameNameButDifferentDomains()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/', 'foo.example.com'));
+        $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/', 'bar.example.com'));
+
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+        $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://foo.example.com/'));
+        $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://bar.example.com/'));
+    }
+
+    public function testCookieGetWithSubdomain()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/', '.example.com'));
+        $cookieJar->set($cookie2 = new Cookie('foo1', 'bar', null, '/', 'test.example.com'));
+
+        $this->assertEquals($cookie1, $cookieJar->get('foo', '/', 'foo.example.com'));
+        $this->assertEquals($cookie1, $cookieJar->get('foo', '/', 'example.com'));
+        $this->assertEquals($cookie2, $cookieJar->get('foo1', '/', 'test.example.com'));
+    }
+
+    public function testCookieGetWithSubdirectory()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/test', '.example.com'));
+        $cookieJar->set($cookie2 = new Cookie('foo1', 'bar1', null, '/', '.example.com'));
+
+        $this->assertNull($cookieJar->get('foo', '/', '.example.com'));
+        $this->assertNull($cookieJar->get('foo', '/bar', '.example.com'));
+        $this->assertEquals($cookie1, $cookieJar->get('foo', '/test', 'example.com'));
+        $this->assertEquals($cookie2, $cookieJar->get('foo1', '/', 'example.com'));
+        $this->assertEquals($cookie2, $cookieJar->get('foo1', '/bar', 'example.com'));
+    }
+
+    public function testCookieWithWildcardDomain()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set(new Cookie('foo', 'bar', null, '/', '.example.com'));
+
+        $this->assertEquals(array('foo' => 'bar'), $cookieJar->allValues('http://www.example.com'));
+        $this->assertEmpty($cookieJar->allValues('http://wwwexample.com'));
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php
new file mode 100644
index 0000000..8e3578a
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php
@@ -0,0 +1,179 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Cookie;
+
+class CookieTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getTestsForToFromString
+     */
+    public function testToFromString($cookie, $url = null)
+    {
+        $this->assertEquals($cookie, (string) Cookie::fromString($cookie, $url));
+    }
+
+    public function getTestsForToFromString()
+    {
+        return array(
+            array('foo=bar; path=/'),
+            array('foo=bar; path=/foo'),
+            array('foo=bar; domain=google.com; path=/'),
+            array('foo=bar; domain=example.com; path=/; secure', 'https://example.com/'),
+            array('foo=bar; path=/; httponly'),
+            array('foo=bar; domain=google.com; path=/foo; secure; httponly', 'https://google.com/'),
+            array('foo=bar=baz; path=/'),
+            array('foo=bar%3Dbaz; path=/'),
+        );
+    }
+
+    public function testFromStringIgnoreSecureFlag()
+    {
+        $this->assertFalse(Cookie::fromString('foo=bar; secure')->isSecure());
+        $this->assertFalse(Cookie::fromString('foo=bar; secure', 'http://example.com/')->isSecure());
+    }
+
+    /**
+     * @dataProvider getExpireCookieStrings
+     */
+    public function testFromStringAcceptsSeveralExpiresDateFormats($cookie)
+    {
+        $this->assertEquals(1596185377, Cookie::fromString($cookie)->getExpiresTime());
+    }
+
+    public function getExpireCookieStrings()
+    {
+        return array(
+            array('foo=bar; expires=Fri, 31-Jul-2020 08:49:37 GMT'),
+            array('foo=bar; expires=Fri, 31 Jul 2020 08:49:37 GMT'),
+            array('foo=bar; expires=Fri, 31-07-2020 08:49:37 GMT'),
+            array('foo=bar; expires=Fri, 31-07-20 08:49:37 GMT'),
+            array('foo=bar; expires=Friday, 31-Jul-20 08:49:37 GMT'),
+            array('foo=bar; expires=Fri Jul 31 08:49:37 2020'),
+            array('foo=bar; expires=\'Fri Jul 31 08:49:37 2020\''),
+            array('foo=bar; expires=Friday July 31st 2020, 08:49:37 GMT'),
+        );
+    }
+
+    public function testFromStringWithCapitalization()
+    {
+        $this->assertEquals('Foo=Bar; path=/', (string) Cookie::fromString('Foo=Bar'));
+        $this->assertEquals('foo=bar; expires=Fri, 31 Dec 2010 23:59:59 GMT; path=/', (string) Cookie::fromString('foo=bar; Expires=Fri, 31 Dec 2010 23:59:59 GMT'));
+        $this->assertEquals('foo=bar; domain=www.example.org; path=/; httponly', (string) Cookie::fromString('foo=bar; DOMAIN=www.example.org; HttpOnly'));
+    }
+
+    public function testFromStringWithUrl()
+    {
+        $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com/'));
+        $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com'));
+        $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com?foo'));
+        $this->assertEquals('foo=bar; domain=www.example.com; path=/foo', (string) Cookie::FromString('foo=bar', 'http://www.example.com/foo/bar'));
+        $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar; path=/', 'http://www.example.com/foo/bar'));
+        $this->assertEquals('foo=bar; domain=www.myotherexample.com; path=/', (string) Cookie::FromString('foo=bar; domain=www.myotherexample.com', 'http://www.example.com/'));
+    }
+
+    public function testFromStringThrowsAnExceptionIfCookieIsNotValid()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        Cookie::FromString('foo');
+    }
+
+    public function testFromStringThrowsAnExceptionIfCookieDateIsNotValid()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        Cookie::FromString('foo=bar; expires=Flursday July 31st 2020, 08:49:37 GMT');
+    }
+
+    public function testFromStringThrowsAnExceptionIfUrlIsNotValid()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        Cookie::FromString('foo=bar', 'foobar');
+    }
+
+    public function testGetName()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertEquals('foo', $cookie->getName(), '->getName() returns the cookie name');
+    }
+
+    public function testGetValue()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertEquals('bar', $cookie->getValue(), '->getValue() returns the cookie value');
+
+        $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value
+        $this->assertEquals('bar=baz', $cookie->getValue(), '->getValue() returns the urldecoded cookie value');
+    }
+
+    public function testGetRawValue()
+    {
+        $cookie = new Cookie('foo', 'bar=baz'); // decoded value
+        $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the urlencoded cookie value');
+        $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value
+        $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the non-urldecoded cookie value');
+    }
+
+    public function testGetPath()
+    {
+        $cookie = new Cookie('foo', 'bar', 0);
+        $this->assertEquals('/', $cookie->getPath(), '->getPath() returns / is no path is defined');
+
+        $cookie = new Cookie('foo', 'bar', 0, '/foo');
+        $this->assertEquals('/foo', $cookie->getPath(), '->getPath() returns the cookie path');
+    }
+
+    public function testGetDomain()
+    {
+        $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com');
+        $this->assertEquals('foo.com', $cookie->getDomain(), '->getDomain() returns the cookie domain');
+    }
+
+    public function testIsSecure()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertFalse($cookie->isSecure(), '->isSecure() returns false if not defined');
+
+        $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', true);
+        $this->assertTrue($cookie->isSecure(), '->isSecure() returns the cookie secure flag');
+    }
+
+    public function testIsHttponly()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns false if not defined');
+
+        $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', false, true);
+        $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns the cookie httponly flag');
+    }
+
+    public function testGetExpiresTime()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertNull($cookie->getExpiresTime(), '->getExpiresTime() returns the expires time');
+
+        $cookie = new Cookie('foo', 'bar', $time = time() - 86400);
+        $this->assertEquals($time, $cookie->getExpiresTime(), '->getExpiresTime() returns the expires time');
+    }
+
+    public function testIsExpired()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertFalse($cookie->isExpired(), '->isExpired() returns false when the cookie never expires (null as expires time)');
+
+        $cookie = new Cookie('foo', 'bar', time() - 86400);
+        $this->assertTrue($cookie->isExpired(), '->isExpired() returns true when the cookie is expired');
+
+        $cookie = new Cookie('foo', 'bar', 0);
+        $this->assertFalse($cookie->isExpired());
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php
new file mode 100644
index 0000000..882b730
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php
@@ -0,0 +1,101 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\History;
+use Symfony\Component\BrowserKit\Request;
+
+class HistoryTest extends \PHPUnit_Framework_TestCase
+{
+    public function testAdd()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example1.com/', 'get'));
+        $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->add() adds a request to the history');
+
+        $history->add(new Request('http://www.example2.com/', 'get'));
+        $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history');
+
+        $history->add(new Request('http://www.example3.com/', 'get'));
+        $history->back();
+        $history->add(new Request('http://www.example4.com/', 'get'));
+        $this->assertSame('http://www.example4.com/', $history->current()->getUri(), '->add() adds a request to the history');
+
+        $history->back();
+        $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history');
+    }
+
+    public function testClearIsEmpty()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example.com/', 'get'));
+
+        $this->assertFalse($history->isEmpty(), '->isEmpty() returns false if the history is not empty');
+
+        $history->clear();
+
+        $this->assertTrue($history->isEmpty(), '->isEmpty() true if the history is empty');
+    }
+
+    public function testCurrent()
+    {
+        $history = new History();
+
+        try {
+            $history->current();
+            $this->fail('->current() throws a \LogicException if the history is empty');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->current() throws a \LogicException if the history is empty');
+        }
+
+        $history->add(new Request('http://www.example.com/', 'get'));
+
+        $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->current() returns the current request in the history');
+    }
+
+    public function testBack()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example.com/', 'get'));
+
+        try {
+            $history->back();
+            $this->fail('->back() throws a \LogicException if the history is already on the first page');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->current() throws a \LogicException if the history is already on the first page');
+        }
+
+        $history->add(new Request('http://www.example1.com/', 'get'));
+        $history->back();
+
+        $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->back() returns the previous request in the history');
+    }
+
+    public function testForward()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example.com/', 'get'));
+        $history->add(new Request('http://www.example1.com/', 'get'));
+
+        try {
+            $history->forward();
+            $this->fail('->forward() throws a \LogicException if the history is already on the last page');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->forward() throws a \LogicException if the history is already on the last page');
+        }
+
+        $history->back();
+        $history->forward();
+
+        $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->forward() returns the next request in the history');
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php
new file mode 100644
index 0000000..b75b5fb
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Request;
+
+class RequestTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGetUri()
+    {
+        $request = new Request('http://www.example.com/', 'get');
+        $this->assertEquals('http://www.example.com/', $request->getUri(), '->getUri() returns the URI of the request');
+    }
+
+    public function testGetMethod()
+    {
+        $request = new Request('http://www.example.com/', 'get');
+        $this->assertEquals('get', $request->getMethod(), '->getMethod() returns the method of the request');
+    }
+
+    public function testGetParameters()
+    {
+        $request = new Request('http://www.example.com/', 'get', array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getParameters(), '->getParameters() returns the parameters of the request');
+    }
+
+    public function testGetFiles()
+    {
+        $request = new Request('http://www.example.com/', 'get', array(), array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getFiles(), '->getFiles() returns the uploaded files of the request');
+    }
+
+    public function testGetCookies()
+    {
+        $request = new Request('http://www.example.com/', 'get', array(), array(), array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getCookies(), '->getCookies() returns the cookies of the request');
+    }
+
+    public function testGetServer()
+    {
+        $request = new Request('http://www.example.com/', 'get', array(), array(), array(), array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getServer(), '->getServer() returns the server parameters of the request');
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php
new file mode 100644
index 0000000..bfe3cd5
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Response;
+
+class ResponseTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGetUri()
+    {
+        $response = new Response('foo');
+        $this->assertEquals('foo', $response->getContent(), '->getContent() returns the content of the response');
+    }
+
+    public function testGetStatus()
+    {
+        $response = new Response('foo', 304);
+        $this->assertEquals('304', $response->getStatus(), '->getStatus() returns the status of the response');
+    }
+
+    public function testGetHeaders()
+    {
+        $response = new Response('foo', 200, array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $response->getHeaders(), '->getHeaders() returns the headers of the response');
+    }
+
+    public function testGetHeader()
+    {
+        $response = new Response('foo', 200, array(
+            'Content-Type' => 'text/html',
+            'Set-Cookie' => array('foo=bar', 'bar=foo'),
+        ));
+
+        $this->assertEquals('text/html', $response->getHeader('Content-Type'), '->getHeader() returns a header of the response');
+        $this->assertEquals('text/html', $response->getHeader('content-type'), '->getHeader() returns a header of the response');
+        $this->assertEquals('text/html', $response->getHeader('content_type'), '->getHeader() returns a header of the response');
+        $this->assertEquals('foo=bar', $response->getHeader('Set-Cookie'), '->getHeader() returns the first header value');
+        $this->assertEquals(array('foo=bar', 'bar=foo'), $response->getHeader('Set-Cookie', false), '->getHeader() returns all header values if first is false');
+
+        $this->assertNull($response->getHeader('foo'), '->getHeader() returns null if the header is not defined');
+        $this->assertEquals(array(), $response->getHeader('foo', false), '->getHeader() returns an empty array if the header is not defined and first is set to false');
+    }
+
+    public function testMagicToString()
+    {
+        $response = new Response('foo', 304, array('foo' => 'bar'));
+
+        $this->assertEquals("foo: bar\n\nfoo", $response->__toString(), '->__toString() returns the headers and the content as a string');
+    }
+
+    public function testMagicToStringWithMultipleSetCookieHeader()
+    {
+        $headers = array(
+            'content-type' => 'text/html; charset=utf-8',
+            'set-cookie' => array('foo=bar', 'bar=foo'),
+        );
+
+        $expected = 'content-type: text/html; charset=utf-8'."\n";
+        $expected .= 'set-cookie: foo=bar'."\n";
+        $expected .= 'set-cookie: bar=foo'."\n\n";
+        $expected .= 'foo';
+
+        $response = new Response('foo', 304, $headers);
+
+        $this->assertEquals($expected, $response->__toString(), '->__toString() returns the headers and the content as a string');
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json
new file mode 100644
index 0000000..aa9f119
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json
@@ -0,0 +1,39 @@
+{
+    "name": "symfony/browser-kit",
+    "type": "library",
+    "description": "Symfony BrowserKit Component",
+    "keywords": [],
+    "homepage": "http://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3",
+        "symfony/dom-crawler": "~2.0,>=2.0.5"
+    },
+    "require-dev": {
+        "symfony/process": "~2.0,>=2.0.5",
+        "symfony/css-selector": "~2.0,>=2.0.5"
+    },
+    "suggest": {
+        "symfony/process": ""
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\BrowserKit\\": "" }
+    },
+    "target-dir": "Symfony/Component/BrowserKit",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.6-dev"
+        }
+    }
+}
diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist
new file mode 100644
index 0000000..78f8da9
--- /dev/null
+++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+>
+    <php>
+        <!-- Silence E_USER_DEPRECATED (-16385 == -1 & ~E_USER_DEPRECATED) -->
+        <ini name="error_reporting" value="-16385"/>
+    </php>
+    <testsuites>
+        <testsuite name="Symfony BrowserKit Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md
new file mode 100644
index 0000000..48fd323
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md
@@ -0,0 +1,45 @@
+CHANGELOG
+=========
+
+2.5.0
+-----
+
+* [BC BREAK] The default value for checkbox and radio inputs without a value attribute have changed
+  from '1' to 'on' to match the HTML specification.
+* [BC BREAK] The typehints on the `Link`, `Form` and `FormField` classes have been changed from
+  `\DOMNode` to `DOMElement`. Using any other type of `DOMNode` was triggering fatal errors in previous
+  versions. Code extending these classes will need to update the typehints when overwriting these methods.
+
+2.4.0
+-----
+
+ * `Crawler::addXmlContent()` removes the default document namespace again if it's an only namespace.
+ * added support for automatic discovery and explicit registration of document
+   namespaces for `Crawler::filterXPath()` and `Crawler::filter()`
+ * improved content type guessing in `Crawler::addContent()`
+ * [BC BREAK] `Crawler::addXmlContent()` no longer removes the default document
+   namespace
+
+2.3.0
+-----
+
+ * added Crawler::html()
+ * [BC BREAK] Crawler::each() and Crawler::reduce() now return Crawler instances instead of DomElement instances
+ * added schema relative URL support to links
+ * added support for HTML5 'form' attribute
+
+2.2.0
+-----
+
+ * added a way to set raw path to the file in FileFormField - necessary for
+   simulating HTTP requests
+
+2.1.0
+-----
+
+ * added support for the HTTP PATCH method
+ * refactored the Form class internals to support multi-dimensional fields
+   (the public API is backward compatible)
+ * added a way to get parsing errors for Crawler::addHtmlContent() and
+   Crawler::addXmlContent() via libxml functions
+ * added support for submitting a form without a submit button
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php
new file mode 100644
index 0000000..9471d54
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php
@@ -0,0 +1,1035 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+use Symfony\Component\CssSelector\CssSelector;
+
+/**
+ * Crawler eases navigation of a list of \DOMElement objects.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Crawler extends \SplObjectStorage
+{
+    /**
+     * @var string The current URI
+     */
+    protected $uri;
+
+    /**
+     * @var string The default namespace prefix to be used with XPath and CSS expressions
+     */
+    private $defaultNamespacePrefix = 'default';
+
+    /**
+     * @var array A map of manually registered namespaces
+     */
+    private $namespaces = array();
+
+    /**
+     * @var string The base href value
+     */
+    private $baseHref;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed  $node A Node to use as the base for the crawling
+     * @param string $currentUri The current URI
+     * @param string $baseHref The base href value
+     * @api
+     */
+    public function __construct($node = null, $currentUri = null, $baseHref = null)
+    {
+        $this->uri = $currentUri;
+        $this->baseHref = $baseHref ?: $currentUri;
+
+        $this->add($node);
+    }
+
+    /**
+     * Removes all the nodes.
+     *
+     * @api
+     */
+    public function clear()
+    {
+        $this->removeAll($this);
+    }
+
+    /**
+     * Adds a node to the current list of nodes.
+     *
+     * This method uses the appropriate specialized add*() method based
+     * on the type of the argument.
+     *
+     * @param \DOMNodeList|\DOMNode|array|string|null $node A node
+     *
+     * @throws \InvalidArgumentException When node is not the expected type.
+     *
+     * @api
+     */
+    public function add($node)
+    {
+        if ($node instanceof \DOMNodeList) {
+            $this->addNodeList($node);
+        } elseif ($node instanceof \DOMNode) {
+            $this->addNode($node);
+        } elseif (is_array($node)) {
+            $this->addNodes($node);
+        } elseif (is_string($node)) {
+            $this->addContent($node);
+        } elseif (null !== $node) {
+            throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', is_object($node) ? get_class($node) : gettype($node)));
+        }
+    }
+
+    /**
+     * Adds HTML/XML content.
+     *
+     * If the charset is not set via the content type, it is assumed
+     * to be ISO-8859-1, which is the default charset defined by the
+     * HTTP 1.1 specification.
+     *
+     * @param string      $content A string to parse as HTML/XML
+     * @param null|string $type    The content type of the string
+     */
+    public function addContent($content, $type = null)
+    {
+        if (empty($type)) {
+            $type = 0 === strpos($content, '<?xml') ? 'application/xml' : 'text/html';
+        }
+
+        // DOM only for HTML/XML content
+        if (!preg_match('/(x|ht)ml/i', $type, $xmlMatches)) {
+            return;
+        }
+
+        $charset = null;
+        if (false !== $pos = stripos($type, 'charset=')) {
+            $charset = substr($type, $pos + 8);
+            if (false !== $pos = strpos($charset, ';')) {
+                $charset = substr($charset, 0, $pos);
+            }
+        }
+
+        // http://www.w3.org/TR/encoding/#encodings
+        // http://www.w3.org/TR/REC-xml/#NT-EncName
+        if (null === $charset &&
+            preg_match('/\<meta[^\>]+charset *= *["\']?([a-zA-Z\-0-9_:.]+)/i', $content, $matches)) {
+            $charset = $matches[1];
+        }
+
+        if (null === $charset) {
+            $charset = 'ISO-8859-1';
+        }
+
+        if ('x' === $xmlMatches[1]) {
+            $this->addXmlContent($content, $charset);
+        } else {
+            $this->addHtmlContent($content, $charset);
+        }
+    }
+
+    /**
+     * Adds an HTML content to the list of nodes.
+     *
+     * The libxml errors are disabled when the content is parsed.
+     *
+     * If you want to get parsing errors, be sure to enable
+     * internal errors via libxml_use_internal_errors(true)
+     * and then, get the errors via libxml_get_errors(). Be
+     * sure to clear errors with libxml_clear_errors() afterward.
+     *
+     * @param string $content The HTML content
+     * @param string $charset The charset
+     *
+     * @api
+     */
+    public function addHtmlContent($content, $charset = 'UTF-8')
+    {
+        $internalErrors = libxml_use_internal_errors(true);
+        $disableEntities = libxml_disable_entity_loader(true);
+
+        $dom = new \DOMDocument('1.0', $charset);
+        $dom->validateOnParse = true;
+
+        if (function_exists('mb_convert_encoding')) {
+            $hasError = false;
+            set_error_handler(function () use (&$hasError) {
+                $hasError = true;
+            });
+            $tmpContent = @mb_convert_encoding($content, 'HTML-ENTITIES', $charset);
+
+            restore_error_handler();
+
+            if (!$hasError) {
+                $content = $tmpContent;
+            }
+        }
+
+        if ('' !== trim($content)) {
+            @$dom->loadHTML($content);
+        }
+
+        libxml_use_internal_errors($internalErrors);
+        libxml_disable_entity_loader($disableEntities);
+
+        $this->addDocument($dom);
+
+        $base = $this->filterRelativeXPath('descendant-or-self::base')->extract(array('href'));
+
+        $baseHref = current($base);
+        if (count($base) && !empty($baseHref)) {
+            if ($this->baseHref) {
+                $linkNode = $dom->createElement('a');
+                $linkNode->setAttribute('href', $baseHref);
+                $link = new Link($linkNode, $this->baseHref);
+                $this->baseHref = $link->getUri();
+            } else {
+                $this->baseHref = $baseHref;
+            }
+        }
+    }
+
+    /**
+     * Adds an XML content to the list of nodes.
+     *
+     * The libxml errors are disabled when the content is parsed.
+     *
+     * If you want to get parsing errors, be sure to enable
+     * internal errors via libxml_use_internal_errors(true)
+     * and then, get the errors via libxml_get_errors(). Be
+     * sure to clear errors with libxml_clear_errors() afterward.
+     *
+     * @param string $content The XML content
+     * @param string $charset The charset
+     *
+     * @api
+     */
+    public function addXmlContent($content, $charset = 'UTF-8')
+    {
+        // remove the default namespace if it's the only namespace to make XPath expressions simpler
+        if (!preg_match('/xmlns:/', $content)) {
+            $content = str_replace('xmlns', 'ns', $content);
+        }
+
+        $internalErrors = libxml_use_internal_errors(true);
+        $disableEntities = libxml_disable_entity_loader(true);
+
+        $dom = new \DOMDocument('1.0', $charset);
+        $dom->validateOnParse = true;
+
+        if ('' !== trim($content)) {
+            @$dom->loadXML($content, LIBXML_NONET);
+        }
+
+        libxml_use_internal_errors($internalErrors);
+        libxml_disable_entity_loader($disableEntities);
+
+        $this->addDocument($dom);
+    }
+
+    /**
+     * Adds a \DOMDocument to the list of nodes.
+     *
+     * @param \DOMDocument $dom A \DOMDocument instance
+     *
+     * @api
+     */
+    public function addDocument(\DOMDocument $dom)
+    {
+        if ($dom->documentElement) {
+            $this->addNode($dom->documentElement);
+        }
+    }
+
+    /**
+     * Adds a \DOMNodeList to the list of nodes.
+     *
+     * @param \DOMNodeList $nodes A \DOMNodeList instance
+     *
+     * @api
+     */
+    public function addNodeList(\DOMNodeList $nodes)
+    {
+        foreach ($nodes as $node) {
+            if ($node instanceof \DOMNode) {
+                $this->addNode($node);
+            }
+        }
+    }
+
+    /**
+     * Adds an array of \DOMNode instances to the list of nodes.
+     *
+     * @param \DOMNode[] $nodes An array of \DOMNode instances
+     *
+     * @api
+     */
+    public function addNodes(array $nodes)
+    {
+        foreach ($nodes as $node) {
+            $this->add($node);
+        }
+    }
+
+    /**
+     * Adds a \DOMNode instance to the list of nodes.
+     *
+     * @param \DOMNode $node A \DOMNode instance
+     *
+     * @api
+     */
+    public function addNode(\DOMNode $node)
+    {
+        if ($node instanceof \DOMDocument) {
+            $this->attach($node->documentElement);
+        } else {
+            $this->attach($node);
+        }
+    }
+
+    /**
+     * Returns a node given its position in the node list.
+     *
+     * @param int $position The position
+     *
+     * @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist.
+     *
+     * @api
+     */
+    public function eq($position)
+    {
+        foreach ($this as $i => $node) {
+            if ($i == $position) {
+                return new static($node, $this->uri, $this->baseHref);
+            }
+        }
+
+        return new static(null, $this->uri, $this->baseHref);
+    }
+
+    /**
+     * Calls an anonymous function on each node of the list.
+     *
+     * The anonymous function receives the position and the node wrapped
+     * in a Crawler instance as arguments.
+     *
+     * Example:
+     *
+     *     $crawler->filter('h1')->each(function ($node, $i) {
+     *         return $node->text();
+     *     });
+     *
+     * @param \Closure $closure An anonymous function
+     *
+     * @return array An array of values returned by the anonymous function
+     *
+     * @api
+     */
+    public function each(\Closure $closure)
+    {
+        $data = array();
+        foreach ($this as $i => $node) {
+            $data[] = $closure(new static($node, $this->uri, $this->baseHref), $i);
+        }
+
+        return $data;
+    }
+
+    /**
+     * Slices the list of nodes by $offset and $length.
+     *
+     * @param int $offset
+     * @param int $length
+     *
+     * @return Crawler A Crawler instance with the sliced nodes
+     */
+    public function slice($offset = 0, $length = -1)
+    {
+        return new static(iterator_to_array(new \LimitIterator($this, $offset, $length)), $this->uri);
+    }
+
+    /**
+     * Reduces the list of nodes by calling an anonymous function.
+     *
+     * To remove a node from the list, the anonymous function must return false.
+     *
+     * @param \Closure $closure An anonymous function
+     *
+     * @return Crawler A Crawler instance with the selected nodes.
+     *
+     * @api
+     */
+    public function reduce(\Closure $closure)
+    {
+        $nodes = array();
+        foreach ($this as $i => $node) {
+            if (false !== $closure(new static($node, $this->uri, $this->baseHref), $i)) {
+                $nodes[] = $node;
+            }
+        }
+
+        return new static($nodes, $this->uri, $this->baseHref);
+    }
+
+    /**
+     * Returns the first node of the current selection.
+     *
+     * @return Crawler A Crawler instance with the first selected node
+     *
+     * @api
+     */
+    public function first()
+    {
+        return $this->eq(0);
+    }
+
+    /**
+     * Returns the last node of the current selection.
+     *
+     * @return Crawler A Crawler instance with the last selected node
+     *
+     * @api
+     */
+    public function last()
+    {
+        return $this->eq(count($this) - 1);
+    }
+
+    /**
+     * Returns the siblings nodes of the current selection.
+     *
+     * @return Crawler A Crawler instance with the sibling nodes
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function siblings()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return new static($this->sibling($this->getNode(0)->parentNode->firstChild), $this->uri, $this->baseHref);
+    }
+
+    /**
+     * Returns the next siblings nodes of the current selection.
+     *
+     * @return Crawler A Crawler instance with the next sibling nodes
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function nextAll()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return new static($this->sibling($this->getNode(0)), $this->uri, $this->baseHref);
+    }
+
+    /**
+     * Returns the previous sibling nodes of the current selection.
+     *
+     * @return Crawler A Crawler instance with the previous sibling nodes
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @api
+     */
+    public function previousAll()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return new static($this->sibling($this->getNode(0), 'previousSibling'), $this->uri, $this->baseHref);
+    }
+
+    /**
+     * Returns the parents nodes of the current selection.
+     *
+     * @return Crawler A Crawler instance with the parents nodes of the current selection
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function parents()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $node = $this->getNode(0);
+        $nodes = array();
+
+        while ($node = $node->parentNode) {
+            if (1 === $node->nodeType) {
+                $nodes[] = $node;
+            }
+        }
+
+        return new static($nodes, $this->uri, $this->baseHref);
+    }
+
+    /**
+     * Returns the children nodes of the current selection.
+     *
+     * @return Crawler A Crawler instance with the children nodes
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function children()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $node = $this->getNode(0)->firstChild;
+
+        return new static($node ? $this->sibling($node) : array(), $this->uri, $this->baseHref);
+    }
+
+    /**
+     * Returns the attribute value of the first node of the list.
+     *
+     * @param string $attribute The attribute name
+     *
+     * @return string|null The attribute value or null if the attribute does not exist
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function attr($attribute)
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $node = $this->getNode(0);
+
+        return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null;
+    }
+
+    /**
+     * Returns the node name of the first node of the list.
+     *
+     * @return string The node name
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     */
+    public function nodeName()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return $this->getNode(0)->nodeName;
+    }
+
+    /**
+     * Returns the node value of the first node of the list.
+     *
+     * @return string The node value
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function text()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return $this->getNode(0)->nodeValue;
+    }
+
+    /**
+     * Returns the first node of the list as HTML.
+     *
+     * @return string The node html
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     */
+    public function html()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $html = '';
+        foreach ($this->getNode(0)->childNodes as $child) {
+            if (PHP_VERSION_ID >= 50306) {
+                // node parameter was added to the saveHTML() method in PHP 5.3.6
+                // @see http://php.net/manual/en/domdocument.savehtml.php
+                $html .= $child->ownerDocument->saveHTML($child);
+            } else {
+                $document = new \DOMDocument('1.0', 'UTF-8');
+                $document->appendChild($document->importNode($child, true));
+                $html .= rtrim($document->saveHTML());
+            }
+        }
+
+        return $html;
+    }
+
+    /**
+     * Extracts information from the list of nodes.
+     *
+     * You can extract attributes or/and the node value (_text).
+     *
+     * Example:
+     *
+     * $crawler->filter('h1 a')->extract(array('_text', 'href'));
+     *
+     * @param array $attributes An array of attributes
+     *
+     * @return array An array of extracted values
+     *
+     * @api
+     */
+    public function extract($attributes)
+    {
+        $attributes = (array) $attributes;
+        $count = count($attributes);
+
+        $data = array();
+        foreach ($this as $node) {
+            $elements = array();
+            foreach ($attributes as $attribute) {
+                if ('_text' === $attribute) {
+                    $elements[] = $node->nodeValue;
+                } else {
+                    $elements[] = $node->getAttribute($attribute);
+                }
+            }
+
+            $data[] = $count > 1 ? $elements : $elements[0];
+        }
+
+        return $data;
+    }
+
+    /**
+     * Filters the list of nodes with an XPath expression.
+     *
+     * The XPath expression is evaluated in the context of the crawler, which
+     * is considered as a fake parent of the elements inside it.
+     * This means that a child selector "div" or "./div" will match only
+     * the div elements of the current crawler, not their children.
+     *
+     * @param string $xpath An XPath expression
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @api
+     */
+    public function filterXPath($xpath)
+    {
+        $xpath = $this->relativize($xpath);
+
+        // If we dropped all expressions in the XPath while preparing it, there would be no match
+        if ('' === $xpath) {
+            return new static(null, $this->uri, $this->baseHref);
+        }
+
+        return $this->filterRelativeXPath($xpath);
+    }
+
+    /**
+     * Filters the list of nodes with a CSS selector.
+     *
+     * This method only works if you have installed the CssSelector Symfony Component.
+     *
+     * @param string $selector A CSS selector
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @throws \RuntimeException if the CssSelector Component is not available
+     *
+     * @api
+     */
+    public function filter($selector)
+    {
+        if (!class_exists('Symfony\\Component\\CssSelector\\CssSelector')) {
+            throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector is not installed (you can use filterXPath instead).');
+        }
+
+        // The CssSelector already prefixes the selector with descendant-or-self::
+        return $this->filterRelativeXPath(CssSelector::toXPath($selector));
+    }
+
+    /**
+     * Selects links by name or alt value for clickable images.
+     *
+     * @param string $value The link text
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @api
+     */
+    public function selectLink($value)
+    {
+        $xpath = sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) ', static::xpathLiteral(' '.$value.' ')).
+                            sprintf('or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]]', static::xpathLiteral(' '.$value.' '));
+
+        return $this->filterRelativeXPath($xpath);
+    }
+
+    /**
+     * Selects a button by name or alt value for images.
+     *
+     * @param string $value The button text
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @api
+     */
+    public function selectButton($value)
+    {
+        $translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")';
+        $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, $translate, static::xpathLiteral(' '.$value.' ')).
+                         sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)).
+                         sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value));
+
+        return $this->filterRelativeXPath($xpath);
+    }
+
+    /**
+     * Returns a Link object for the first node in the list.
+     *
+     * @param string $method The method for the link (get by default)
+     *
+     * @return Link A Link instance
+     *
+     * @throws \InvalidArgumentException If the current node list is empty
+     *
+     * @api
+     */
+    public function link($method = 'get')
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $node = $this->getNode(0);
+
+        return new Link($node, $this->baseHref, $method);
+    }
+
+    /**
+     * Returns an array of Link objects for the nodes in the list.
+     *
+     * @return Link[] An array of Link instances
+     *
+     * @api
+     */
+    public function links()
+    {
+        $links = array();
+        foreach ($this as $node) {
+            $links[] = new Link($node, $this->baseHref, 'get');
+        }
+
+        return $links;
+    }
+
+    /**
+     * Returns a Form object for the first node in the list.
+     *
+     * @param array  $values An array of values for the form fields
+     * @param string $method The method for the form
+     *
+     * @return Form A Form instance
+     *
+     * @throws \InvalidArgumentException If the current node list is empty
+     *
+     * @api
+     */
+    public function form(array $values = null, $method = null)
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $form = new Form($this->getNode(0), $this->uri, $method);
+
+        if (null !== $values) {
+            $form->setValues($values);
+        }
+
+        return $form;
+    }
+
+    /**
+     * Overloads a default namespace prefix to be used with XPath and CSS expressions.
+     *
+     * @param string $prefix
+     */
+    public function setDefaultNamespacePrefix($prefix)
+    {
+        $this->defaultNamespacePrefix = $prefix;
+    }
+
+    /**
+     * @param string $prefix
+     * @param string $namespace
+     */
+    public function registerNamespace($prefix, $namespace)
+    {
+        $this->namespaces[$prefix] = $namespace;
+    }
+
+    /**
+     * Converts string for XPath expressions.
+     *
+     * Escaped characters are: quotes (") and apostrophe (').
+     *
+     *  Examples:
+     *  <code>
+     *     echo Crawler::xpathLiteral('foo " bar');
+     *     //prints 'foo " bar'
+     *
+     *     echo Crawler::xpathLiteral("foo ' bar");
+     *     //prints "foo ' bar"
+     *
+     *     echo Crawler::xpathLiteral('a\'b"c');
+     *     //prints concat('a', "'", 'b"c')
+     *  </code>
+     *
+     * @param string $s String to be escaped
+     *
+     * @return string Converted string
+     */
+    public static function xpathLiteral($s)
+    {
+        if (false === strpos($s, "'")) {
+            return sprintf("'%s'", $s);
+        }
+
+        if (false === strpos($s, '"')) {
+            return sprintf('"%s"', $s);
+        }
+
+        $string = $s;
+        $parts = array();
+        while (true) {
+            if (false !== $pos = strpos($string, "'")) {
+                $parts[] = sprintf("'%s'", substr($string, 0, $pos));
+                $parts[] = "\"'\"";
+                $string = substr($string, $pos + 1);
+            } else {
+                $parts[] = "'$string'";
+                break;
+            }
+        }
+
+        return sprintf("concat(%s)", implode($parts, ', '));
+    }
+
+    /**
+     * Filters the list of nodes with an XPath expression.
+     *
+     * The XPath expression should already be processed to apply it in the context of each node.
+     *
+     * @param string $xpath
+     *
+     * @return Crawler
+     */
+    private function filterRelativeXPath($xpath)
+    {
+        $prefixes = $this->findNamespacePrefixes($xpath);
+
+        $crawler = new static(null, $this->uri, $this->baseHref);
+
+        foreach ($this as $node) {
+            $domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes);
+            $crawler->add($domxpath->query($xpath, $node));
+        }
+
+        return $crawler;
+    }
+
+    /**
+     * Make the XPath relative to the current context.
+     *
+     * The returned XPath will match elements matching the XPath inside the current crawler
+     * when running in the context of a node of the crawler.
+     *
+     * @param string $xpath
+     *
+     * @return string
+     */
+    private function relativize($xpath)
+    {
+        $expressions = array();
+
+        $unionPattern = '/\|(?![^\[]*\])/';
+        // An expression which will never match to replace expressions which cannot match in the crawler
+        // We cannot simply drop
+        $nonMatchingExpression = 'a[name() = "b"]';
+
+        // Split any unions into individual expressions.
+        foreach (preg_split($unionPattern, $xpath) as $expression) {
+            $expression = trim($expression);
+            $parenthesis = '';
+
+            // If the union is inside some braces, we need to preserve the opening braces and apply
+            // the change only inside it.
+            if (preg_match('/^[\(\s*]+/', $expression, $matches)) {
+                $parenthesis = $matches[0];
+                $expression = substr($expression, strlen($parenthesis));
+            }
+
+            // BC for Symfony 2.4 and lower were elements were adding in a fake _root parent
+            if (0 === strpos($expression, '/_root/')) {
+                $expression = './'.substr($expression, 7);
+            } elseif (0 === strpos($expression, 'self::*/')) {
+                $expression = './'.substr($expression, 8);
+            }
+
+            // add prefix before absolute element selector
+            if (empty($expression)) {
+                $expression = $nonMatchingExpression;
+            } elseif (0 === strpos($expression, '//')) {
+                $expression = 'descendant-or-self::'.substr($expression, 2);
+            } elseif (0 === strpos($expression, './/')) {
+                $expression = 'descendant-or-self::'.substr($expression, 3);
+            } elseif (0 === strpos($expression, './')) {
+                $expression = 'self::'.substr($expression, 2);
+            } elseif (0 === strpos($expression, 'child::')) {
+                $expression = 'self::'.substr($expression, 7);
+            } elseif ('/' === $expression[0] || 0 === strpos($expression, 'self::')) {
+                // the only direct child in Symfony 2.4 and lower is _root, which is already handled previously
+                // so let's drop the expression entirely
+                $expression = $nonMatchingExpression;
+            } elseif ('.' === $expression[0]) {
+                // '.' is the fake root element in Symfony 2.4 and lower, which is excluded from results
+                $expression = $nonMatchingExpression;
+            } elseif (0 === strpos($expression, 'descendant::')) {
+                $expression = 'descendant-or-self::'.substr($expression, strlen('descendant::'));
+            } elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) {
+                // the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes)
+                $expression = $nonMatchingExpression;
+            } elseif (0 !== strpos($expression, 'descendant-or-self::')) {
+                $expression = 'self::'.$expression;
+            }
+            $expressions[] = $parenthesis.$expression;
+        }
+
+        return implode(' | ', $expressions);
+    }
+
+    /**
+     * @param int $position
+     *
+     * @return \DOMElement|null
+     */
+    public function getNode($position)
+    {
+        foreach ($this as $i => $node) {
+            if ($i == $position) {
+                return $node;
+            }
+        }
+    }
+
+    /**
+     * @param \DOMElement $node
+     * @param string      $siblingDir
+     *
+     * @return array
+     */
+    protected function sibling($node, $siblingDir = 'nextSibling')
+    {
+        $nodes = array();
+
+        do {
+            if ($node !== $this->getNode(0) && $node->nodeType === 1) {
+                $nodes[] = $node;
+            }
+        } while ($node = $node->$siblingDir);
+
+        return $nodes;
+    }
+
+    /**
+     * @param \DOMDocument $document
+     * @param array        $prefixes
+     *
+     * @return \DOMXPath
+     *
+     * @throws \InvalidArgumentException
+     */
+    private function createDOMXPath(\DOMDocument $document, array $prefixes = array())
+    {
+        $domxpath = new \DOMXPath($document);
+
+        foreach ($prefixes as $prefix) {
+            $namespace = $this->discoverNamespace($domxpath, $prefix);
+            if (null !== $namespace) {
+                $domxpath->registerNamespace($prefix, $namespace);
+            }
+        }
+
+        return $domxpath;
+    }
+
+    /**
+     * @param \DOMXPath $domxpath
+     * @param string    $prefix
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException
+     */
+    private function discoverNamespace(\DOMXPath $domxpath, $prefix)
+    {
+        if (isset($this->namespaces[$prefix])) {
+            return $this->namespaces[$prefix];
+        }
+
+        // ask for one namespace, otherwise we'd get a collection with an item for each node
+        $namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix));
+
+        if ($node = $namespaces->item(0)) {
+            return $node->nodeValue;
+        }
+    }
+
+    /**
+     * @param $xpath
+     *
+     * @return array
+     */
+    private function findNamespacePrefixes($xpath)
+    {
+        if (preg_match_all('/(?P<prefix>[a-z_][a-z_0-9\-\.]*):[^"\/]/i', $xpath, $matches)) {
+            return array_unique($matches['prefix']);
+        }
+
+        return array();
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php
new file mode 100644
index 0000000..ae7f4d1
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php
@@ -0,0 +1,327 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * ChoiceFormField represents a choice form field.
+ *
+ * It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class ChoiceFormField extends FormField
+{
+    /**
+     * @var string
+     */
+    private $type;
+    /**
+     * @var bool
+     */
+    private $multiple;
+    /**
+     * @var array
+     */
+    private $options;
+    /**
+     * @var bool
+     */
+    private $validationDisabled = false;
+
+    /**
+     * Returns true if the field should be included in the submitted values.
+     *
+     * @return bool true if the field should be included in the submitted values, false otherwise
+     */
+    public function hasValue()
+    {
+        // don't send a value for unchecked checkboxes
+        if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the current selected option is disabled.
+     *
+     * @return bool
+     */
+    public function isDisabled()
+    {
+        foreach ($this->options as $option) {
+            if ($option['value'] == $this->value && $option['disabled']) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @api
+     */
+    public function select($value)
+    {
+        $this->setValue($value);
+    }
+
+    /**
+     * Ticks a checkbox.
+     *
+     * @throws \LogicException When the type provided is not correct
+     *
+     * @api
+     */
+    public function tick()
+    {
+        if ('checkbox' !== $this->type) {
+            throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
+        }
+
+        $this->setValue(true);
+    }
+
+    /**
+     * Ticks a checkbox.
+     *
+     * @throws \LogicException When the type provided is not correct
+     *
+     * @api
+     */
+    public function untick()
+    {
+        if ('checkbox' !== $this->type) {
+            throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
+        }
+
+        $this->setValue(false);
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @throws \InvalidArgumentException When value type provided is not correct
+     */
+    public function setValue($value)
+    {
+        if ('checkbox' === $this->type && false === $value) {
+            // uncheck
+            $this->value = null;
+        } elseif ('checkbox' === $this->type && true === $value) {
+            // check
+            $this->value = $this->options[0]['value'];
+        } else {
+            if (is_array($value)) {
+                if (!$this->multiple) {
+                    throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name));
+                }
+
+                foreach ($value as $v) {
+                    if (!$this->containsOption($v, $this->options)) {
+                        throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues())));
+                    }
+                }
+            } elseif (!$this->containsOption($value, $this->options)) {
+                throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues())));
+            }
+
+            if ($this->multiple) {
+                $value = (array) $value;
+            }
+
+            if (is_array($value)) {
+                $this->value = $value;
+            } else {
+                parent::setValue($value);
+            }
+        }
+    }
+
+    /**
+     * Adds a choice to the current ones.
+     *
+     * This method should only be used internally.
+     *
+     * @param \DOMElement $node
+     *
+     * @throws \LogicException When choice provided is not multiple nor radio
+     */
+    public function addChoice(\DOMElement $node)
+    {
+        if (!$this->multiple && 'radio' !== $this->type) {
+            throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name));
+        }
+
+        $option = $this->buildOptionValue($node);
+        $this->options[] = $option;
+
+        if ($node->hasAttribute('checked')) {
+            $this->value = $option['value'];
+        }
+    }
+
+    /**
+     * Returns the type of the choice field (radio, select, or checkbox).
+     *
+     * @return string The type
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Returns true if the field accepts multiple values.
+     *
+     * @return bool true if the field accepts multiple values, false otherwise
+     */
+    public function isMultiple()
+    {
+        return $this->multiple;
+    }
+
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) {
+            throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName));
+        }
+
+        if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) {
+            throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type')));
+        }
+
+        $this->value = null;
+        $this->options = array();
+        $this->multiple = false;
+
+        if ('input' == $this->node->nodeName) {
+            $this->type = strtolower($this->node->getAttribute('type'));
+            $optionValue = $this->buildOptionValue($this->node);
+            $this->options[] = $optionValue;
+
+            if ($this->node->hasAttribute('checked')) {
+                $this->value = $optionValue['value'];
+            }
+        } else {
+            $this->type = 'select';
+            if ($this->node->hasAttribute('multiple')) {
+                $this->multiple = true;
+                $this->value = array();
+                $this->name = str_replace('[]', '', $this->name);
+            }
+
+            $found = false;
+            foreach ($this->xpath->query('descendant::option', $this->node) as $option) {
+                $optionValue = $this->buildOptionValue($option);
+                $this->options[] = $optionValue;
+
+                if ($option->hasAttribute('selected')) {
+                    $found = true;
+                    if ($this->multiple) {
+                        $this->value[] = $optionValue['value'];
+                    } else {
+                        $this->value = $optionValue['value'];
+                    }
+                }
+            }
+
+            // if no option is selected and if it is a simple select box, take the first option as the value
+            if (!$found && !$this->multiple && !empty($this->options)) {
+                $this->value = $this->options[0]['value'];
+            }
+        }
+    }
+
+    /**
+     * Returns option value with associated disabled flag.
+     *
+     * @param \DOMElement $node
+     *
+     * @return array
+     */
+    private function buildOptionValue(\DOMElement $node)
+    {
+        $option = array();
+
+        $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : 'on';
+        $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue;
+        $option['disabled'] = $node->hasAttribute('disabled');
+
+        return $option;
+    }
+
+    /**
+     * Checks whether given value is in the existing options.
+     *
+     * @param string $optionValue
+     * @param array  $options
+     *
+     * @return bool
+     */
+    public function containsOption($optionValue, $options)
+    {
+        if ($this->validationDisabled) {
+            return true;
+        }
+
+        foreach ($options as $option) {
+            if ($option['value'] == $optionValue) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns list of available field options.
+     *
+     * @return array
+     */
+    public function availableOptionValues()
+    {
+        $values = array();
+
+        foreach ($this->options as $option) {
+            $values[] = $option['value'];
+        }
+
+        return $values;
+    }
+
+    /**
+     * Disables the internal validation of the field.
+     *
+     * @return self
+     */
+    public function disableValidation()
+    {
+        $this->validationDisabled = true;
+
+        return $this;
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php
new file mode 100644
index 0000000..c3423b2
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php
@@ -0,0 +1,112 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * FileFormField represents a file form field (an HTML file input tag).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class FileFormField extends FormField
+{
+    /**
+     * Sets the PHP error code associated with the field.
+     *
+     * @param int $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION)
+     *
+     * @throws \InvalidArgumentException When error code doesn't exist
+     */
+    public function setErrorCode($error)
+    {
+        $codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION);
+        if (!in_array($error, $codes)) {
+            throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error));
+        }
+
+        $this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0);
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @api
+     */
+    public function upload($value)
+    {
+        $this->setValue($value);
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     */
+    public function setValue($value)
+    {
+        if (null !== $value && is_readable($value)) {
+            $error = UPLOAD_ERR_OK;
+            $size = filesize($value);
+            $info = pathinfo($value);
+            $name = $info['basename'];
+
+            // copy to a tmp location
+            $tmp = sys_get_temp_dir().'/'.sha1(uniqid(mt_rand(), true));
+            if (array_key_exists('extension', $info)) {
+                $tmp .= '.'.$info['extension'];
+            }
+            if (is_file($tmp)) {
+                unlink($tmp);
+            }
+            copy($value, $tmp);
+            $value = $tmp;
+        } else {
+            $error = UPLOAD_ERR_NO_FILE;
+            $size = 0;
+            $name = '';
+            $value = '';
+        }
+
+        $this->value = array('name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size);
+    }
+
+    /**
+     * Sets path to the file as string for simulating HTTP request.
+     *
+     * @param string $path The path to the file
+     */
+    public function setFilePath($path)
+    {
+        parent::setValue($path);
+    }
+
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('input' !== $this->node->nodeName) {
+            throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName));
+        }
+
+        if ('file' !== strtolower($this->node->getAttribute('type'))) {
+            throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type')));
+        }
+
+        $this->setValue(null);
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php
new file mode 100644
index 0000000..5d70292
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php
@@ -0,0 +1,116 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * FormField is the abstract class for all form fields.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class FormField
+{
+    /**
+     * @var \DOMElement
+     */
+    protected $node;
+    /**
+     * @var string
+     */
+    protected $name;
+    /**
+     * @var string
+     */
+    protected $value;
+    /**
+     * @var \DOMDocument
+     */
+    protected $document;
+    /**
+     * @var \DOMXPath
+     */
+    protected $xpath;
+    /**
+     * @var bool
+     */
+    protected $disabled;
+
+    /**
+     * Constructor.
+     *
+     * @param \DOMElement $node The node associated with this field
+     */
+    public function __construct(\DOMElement $node)
+    {
+        $this->node = $node;
+        $this->name = $node->getAttribute('name');
+        $this->xpath = new \DOMXPath($node->ownerDocument);
+
+        $this->initialize();
+    }
+
+    /**
+     * Returns the name of the field.
+     *
+     * @return string The name of the field
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the value of the field.
+     *
+     * @return string|array The value of the field
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @api
+     */
+    public function setValue($value)
+    {
+        $this->value = (string) $value;
+    }
+
+    /**
+     * Returns true if the field should be included in the submitted values.
+     *
+     * @return bool true if the field should be included in the submitted values, false otherwise
+     */
+    public function hasValue()
+    {
+        return true;
+    }
+
+    /**
+     * Check if the current field is disabled.
+     *
+     * @return bool
+     */
+    public function isDisabled()
+    {
+        return $this->node->hasAttribute('disabled');
+    }
+
+    /**
+     * Initializes the form field.
+     */
+    abstract protected function initialize();
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php
new file mode 100644
index 0000000..b9bd0a4
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * InputFormField represents an input form field (an HTML input tag).
+ *
+ * For inputs with type of file, checkbox, or radio, there are other more
+ * specialized classes (cf. FileFormField and ChoiceFormField).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class InputFormField extends FormField
+{
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) {
+            throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName));
+        }
+
+        if ('checkbox' === strtolower($this->node->getAttribute('type'))) {
+            throw new \LogicException('Checkboxes should be instances of ChoiceFormField.');
+        }
+
+        if ('file' === strtolower($this->node->getAttribute('type'))) {
+            throw new \LogicException('File inputs should be instances of FileFormField.');
+        }
+
+        $this->value = $this->node->getAttribute('value');
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php
new file mode 100644
index 0000000..a14e707
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * TextareaFormField represents a textarea form field (an HTML textarea tag).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class TextareaFormField extends FormField
+{
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('textarea' !== $this->node->nodeName) {
+            throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName));
+        }
+
+        $this->value = '';
+        foreach ($this->node->childNodes as $node) {
+            $this->value .= $node->wholeText;
+        }
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php
new file mode 100644
index 0000000..528ad36
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php
@@ -0,0 +1,479 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+use Symfony\Component\DomCrawler\Field\ChoiceFormField;
+use Symfony\Component\DomCrawler\Field\FormField;
+
+/**
+ * Form represents an HTML form.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Form extends Link implements \ArrayAccess
+{
+    /**
+     * @var \DOMElement
+     */
+    private $button;
+
+    /**
+     * @var FormFieldRegistry
+     */
+    private $fields;
+
+    /**
+     * Constructor.
+     *
+     * @param \DOMElement $node       A \DOMElement instance
+     * @param string      $currentUri The URI of the page where the form is embedded
+     * @param string      $method     The method to use for the link (if null, it defaults to the method defined by the form)
+     *
+     * @throws \LogicException if the node is not a button inside a form tag
+     *
+     * @api
+     */
+    public function __construct(\DOMElement $node, $currentUri, $method = null)
+    {
+        parent::__construct($node, $currentUri, $method);
+
+        $this->initialize();
+    }
+
+    /**
+     * Gets the form node associated with this form.
+     *
+     * @return \DOMElement A \DOMElement instance
+     */
+    public function getFormNode()
+    {
+        return $this->node;
+    }
+
+    /**
+     * Sets the value of the fields.
+     *
+     * @param array $values An array of field values
+     *
+     * @return Form
+     *
+     * @api
+     */
+    public function setValues(array $values)
+    {
+        foreach ($values as $name => $value) {
+            $this->fields->set($name, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Gets the field values.
+     *
+     * The returned array does not include file fields (@see getFiles).
+     *
+     * @return array An array of field values.
+     *
+     * @api
+     */
+    public function getValues()
+    {
+        $values = array();
+        foreach ($this->fields->all() as $name => $field) {
+            if ($field->isDisabled()) {
+                continue;
+            }
+
+            if (!$field instanceof Field\FileFormField && $field->hasValue()) {
+                $values[$name] = $field->getValue();
+            }
+        }
+
+        return $values;
+    }
+
+    /**
+     * Gets the file field values.
+     *
+     * @return array An array of file field values.
+     *
+     * @api
+     */
+    public function getFiles()
+    {
+        if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) {
+            return array();
+        }
+
+        $files = array();
+
+        foreach ($this->fields->all() as $name => $field) {
+            if ($field->isDisabled()) {
+                continue;
+            }
+
+            if ($field instanceof Field\FileFormField) {
+                $files[$name] = $field->getValue();
+            }
+        }
+
+        return $files;
+    }
+
+    /**
+     * Gets the field values as PHP.
+     *
+     * This method converts fields with the array notation
+     * (like foo[bar] to arrays) like PHP does.
+     *
+     * @return array An array of field values.
+     *
+     * @api
+     */
+    public function getPhpValues()
+    {
+        $values = array();
+        foreach ($this->getValues() as $name => $value) {
+            $qs = http_build_query(array($name => $value), '', '&');
+            if (!empty($qs)) {
+                parse_str($qs, $expandedValue);
+                $varName = substr($name, 0, strlen(key($expandedValue)));
+                $values = array_replace_recursive($values, array($varName => current($expandedValue)));
+            }
+        }
+
+        return $values;
+    }
+
+    /**
+     * Gets the file field values as PHP.
+     *
+     * This method converts fields with the array notation
+     * (like foo[bar] to arrays) like PHP does.
+     *
+     * @return array An array of field values.
+     *
+     * @api
+     */
+    public function getPhpFiles()
+    {
+        $values = array();
+        foreach ($this->getFiles() as $name => $value) {
+            $qs = http_build_query(array($name => $value), '', '&');
+            if (!empty($qs)) {
+                parse_str($qs, $expandedValue);
+                $varName = substr($name, 0, strlen(key($expandedValue)));
+                $values = array_replace_recursive($values, array($varName => current($expandedValue)));
+            }
+        }
+
+        return $values;
+    }
+
+    /**
+     * Gets the URI of the form.
+     *
+     * The returned URI is not the same as the form "action" attribute.
+     * This method merges the value if the method is GET to mimics
+     * browser behavior.
+     *
+     * @return string The URI
+     *
+     * @api
+     */
+    public function getUri()
+    {
+        $uri = parent::getUri();
+
+        if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH')) && $queryString = http_build_query($this->getValues(), null, '&')) {
+            $sep = false === strpos($uri, '?') ? '?' : '&';
+            $uri .= $sep.$queryString;
+        }
+
+        return $uri;
+    }
+
+    protected function getRawUri()
+    {
+        return $this->node->getAttribute('action');
+    }
+
+    /**
+     * Gets the form method.
+     *
+     * If no method is defined in the form, GET is returned.
+     *
+     * @return string The method
+     *
+     * @api
+     */
+    public function getMethod()
+    {
+        if (null !== $this->method) {
+            return $this->method;
+        }
+
+        return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET';
+    }
+
+    /**
+     * Returns true if the named field exists.
+     *
+     * @param string $name The field name
+     *
+     * @return bool true if the field exists, false otherwise
+     *
+     * @api
+     */
+    public function has($name)
+    {
+        return $this->fields->has($name);
+    }
+
+    /**
+     * Removes a field from the form.
+     *
+     * @param string $name The field name
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     *
+     * @api
+     */
+    public function remove($name)
+    {
+        $this->fields->remove($name);
+    }
+
+    /**
+     * Gets a named field.
+     *
+     * @param string $name The field name
+     *
+     * @return FormField The field instance
+     *
+     * @throws \InvalidArgumentException When field is not present in this form
+     *
+     * @api
+     */
+    public function get($name)
+    {
+        return $this->fields->get($name);
+    }
+
+    /**
+     * Sets a named field.
+     *
+     * @param FormField $field The field
+     *
+     * @api
+     */
+    public function set(FormField $field)
+    {
+        $this->fields->add($field);
+    }
+
+    /**
+     * Gets all fields.
+     *
+     * @return FormField[] An array of fields
+     *
+     * @api
+     */
+    public function all()
+    {
+        return $this->fields->all();
+    }
+
+    /**
+     * Returns true if the named field exists.
+     *
+     * @param string $name The field name
+     *
+     * @return bool true if the field exists, false otherwise
+     */
+    public function offsetExists($name)
+    {
+        return $this->has($name);
+    }
+
+    /**
+     * Gets the value of a field.
+     *
+     * @param string $name The field name
+     *
+     * @return FormField The associated Field instance
+     *
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function offsetGet($name)
+    {
+        return $this->fields->get($name);
+    }
+
+    /**
+     * Sets the value of a field.
+     *
+     * @param string       $name  The field name
+     * @param string|array $value The value of the field
+     *
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function offsetSet($name, $value)
+    {
+        $this->fields->set($name, $value);
+    }
+
+    /**
+     * Removes a field from the form.
+     *
+     * @param string $name The field name
+     */
+    public function offsetUnset($name)
+    {
+        $this->fields->remove($name);
+    }
+
+    /**
+     * Disables validation
+     *
+     * @return self
+     */
+    public function disableValidation()
+    {
+        foreach ($this->fields->all() as $field) {
+            if ($field instanceof Field\ChoiceFormField) {
+                $field->disableValidation();
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the node for the form.
+     *
+     * Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself.
+     *
+     * @param \DOMElement $node A \DOMElement instance
+     *
+     * @throws \LogicException If given node is not a button or input or does not have a form ancestor
+     */
+    protected function setNode(\DOMElement $node)
+    {
+        $this->button = $node;
+        if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image')))) {
+            if ($node->hasAttribute('form')) {
+                // if the node has the HTML5-compliant 'form' attribute, use it
+                $formId = $node->getAttribute('form');
+                $form = $node->ownerDocument->getElementById($formId);
+                if (null === $form) {
+                    throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId));
+                }
+                $this->node = $form;
+
+                return;
+            }
+            // we loop until we find a form ancestor
+            do {
+                if (null === $node = $node->parentNode) {
+                    throw new \LogicException('The selected node does not have a form ancestor.');
+                }
+            } while ('form' !== $node->nodeName);
+        } elseif ('form' !== $node->nodeName) {
+            throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
+        }
+
+        $this->node = $node;
+    }
+
+    /**
+     * Adds form elements related to this form.
+     *
+     * Creates an internal copy of the submitted 'button' element and
+     * the form node or the entire document depending on whether we need
+     * to find non-descendant elements through HTML5 'form' attribute.
+     */
+    private function initialize()
+    {
+        $this->fields = new FormFieldRegistry();
+
+        $xpath = new \DOMXPath($this->node->ownerDocument);
+
+        // add submitted button if it has a valid name
+        if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) {
+            if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) {
+                $name = $this->button->getAttribute('name');
+                $this->button->setAttribute('value', '0');
+
+                // temporarily change the name of the input node for the x coordinate
+                $this->button->setAttribute('name', $name.'.x');
+                $this->set(new Field\InputFormField($this->button));
+
+                // temporarily change the name of the input node for the y coordinate
+                $this->button->setAttribute('name', $name.'.y');
+                $this->set(new Field\InputFormField($this->button));
+
+                // restore the original name of the input node
+                $this->button->setAttribute('name', $name);
+            } else {
+                $this->set(new Field\InputFormField($this->button));
+            }
+        }
+
+        // find form elements corresponding to the current form
+        if ($this->node->hasAttribute('id')) {
+            // corresponding elements are either descendants or have a matching HTML5 form attribute
+            $formId = Crawler::xpathLiteral($this->node->getAttribute('id'));
+
+            $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId));
+            foreach ($fieldNodes as $node) {
+                $this->addField($node);
+            }
+        } else {
+            // do the xpath query with $this->node as the context node, to only find descendant elements
+            // however, descendant elements with form attribute are not part of this form
+            $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node);
+            foreach ($fieldNodes as $node) {
+                $this->addField($node);
+            }
+        }
+    }
+
+    private function addField(\DOMElement $node)
+    {
+        if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
+            return;
+        }
+
+        $nodeName = $node->nodeName;
+        if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) {
+            $this->set(new Field\ChoiceFormField($node));
+        } elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) {
+            // there may be other fields with the same name that are no choice
+            // fields already registered (see https://github.com/symfony/symfony/issues/11689)
+            if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) {
+                $this->get($node->getAttribute('name'))->addChoice($node);
+            } else {
+                $this->set(new Field\ChoiceFormField($node));
+            }
+        } elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) {
+            $this->set(new Field\FileFormField($node));
+        } elseif ('input' == $nodeName && !in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image'))) {
+            $this->set(new Field\InputFormField($node));
+        } elseif ('textarea' == $nodeName) {
+            $this->set(new Field\TextareaFormField($node));
+        }
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php
new file mode 100644
index 0000000..95dc878
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php
@@ -0,0 +1,220 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+use Symfony\Component\DomCrawler\Field\FormField;
+
+/**
+ * This is an internal class that must not be used directly.
+ */
+class FormFieldRegistry
+{
+    private $fields = array();
+
+    private $base;
+
+    /**
+     * Adds a field to the registry.
+     *
+     * @param FormField $field The field
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     */
+    public function add(FormField $field)
+    {
+        $segments = $this->getSegments($field->getName());
+
+        $target = & $this->fields;
+        while ($segments) {
+            if (!is_array($target)) {
+                $target = array();
+            }
+            $path = array_shift($segments);
+            if ('' === $path) {
+                $target = & $target[];
+            } else {
+                $target = & $target[$path];
+            }
+        }
+        $target = $field;
+    }
+
+    /**
+     * Removes a field and its children from the registry.
+     *
+     * @param string $name The fully qualified name of the base field
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     */
+    public function remove($name)
+    {
+        $segments = $this->getSegments($name);
+        $target = & $this->fields;
+        while (count($segments) > 1) {
+            $path = array_shift($segments);
+            if (!array_key_exists($path, $target)) {
+                return;
+            }
+            $target = & $target[$path];
+        }
+        unset($target[array_shift($segments)]);
+    }
+
+    /**
+     * Returns the value of the field and its children.
+     *
+     * @param string $name The fully qualified name of the field
+     *
+     * @return mixed The value of the field
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function &get($name)
+    {
+        $segments = $this->getSegments($name);
+        $target = & $this->fields;
+        while ($segments) {
+            $path = array_shift($segments);
+            if (!array_key_exists($path, $target)) {
+                throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path));
+            }
+            $target = & $target[$path];
+        }
+
+        return $target;
+    }
+
+    /**
+     * Tests whether the form has the given field.
+     *
+     * @param string $name The fully qualified name of the field
+     *
+     * @return bool Whether the form has the given field
+     */
+    public function has($name)
+    {
+        try {
+            $this->get($name);
+
+            return true;
+        } catch (\InvalidArgumentException $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Set the value of a field and its children.
+     *
+     * @param string $name  The fully qualified name of the field
+     * @param mixed  $value The value
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function set($name, $value)
+    {
+        $target = & $this->get($name);
+        if (!is_array($value) || $target instanceof Field\ChoiceFormField) {
+            $target->setValue($value);
+        } else {
+            $fields = self::create($name, $value);
+            foreach ($fields->all() as $k => $v) {
+                $this->set($k, $v);
+            }
+        }
+    }
+
+    /**
+     * Returns the list of field with their value.
+     *
+     * @return FormField[] The list of fields as array((string) Fully qualified name => (mixed) value)
+     */
+    public function all()
+    {
+        return $this->walk($this->fields, $this->base);
+    }
+
+    /**
+     * Creates an instance of the class.
+     *
+     * This function is made private because it allows overriding the $base and
+     * the $values properties without any type checking.
+     *
+     * @param string $base   The fully qualified name of the base field
+     * @param array  $values The values of the fields
+     *
+     * @return FormFieldRegistry
+     */
+    private static function create($base, array $values)
+    {
+        $registry = new static();
+        $registry->base = $base;
+        $registry->fields = $values;
+
+        return $registry;
+    }
+
+    /**
+     * Transforms a PHP array in a list of fully qualified name / value.
+     *
+     * @param array  $array  The PHP array
+     * @param string $base   The name of the base field
+     * @param array  $output The initial values
+     *
+     * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+     */
+    private function walk(array $array, $base = '', array &$output = array())
+    {
+        foreach ($array as $k => $v) {
+            $path = empty($base) ? $k : sprintf("%s[%s]", $base, $k);
+            if (is_array($v)) {
+                $this->walk($v, $path, $output);
+            } else {
+                $output[$path] = $v;
+            }
+        }
+
+        return $output;
+    }
+
+    /**
+     * Splits a field name into segments as a web browser would do.
+     *
+     * <code>
+     *     getSegments('base[foo][3][]') = array('base', 'foo, '3', '');
+     * </code>
+     *
+     * @param string $name The name of the field
+     *
+     * @return string[] The list of segments
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     */
+    private function getSegments($name)
+    {
+        if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
+            $segments = array($m['base']);
+            while (!empty($m['extra'])) {
+                if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $m['extra'], $m)) {
+                    $segments[] = $m['segment'];
+                } else {
+                    throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name));
+                }
+            }
+
+            return $segments;
+        }
+
+        throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name));
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE
new file mode 100644
index 0000000..43028bc
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2015 Fabien Potencier
+
+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/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php
new file mode 100644
index 0000000..03149f2
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php
@@ -0,0 +1,232 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+/**
+ * Link represents an HTML link (an HTML a, area or link tag).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Link
+{
+    /**
+     * @var \DOMElement
+     */
+    protected $node;
+
+    /**
+     * @var string The method to use for the link
+     */
+    protected $method;
+
+    /**
+     * @var string The URI of the page where the link is embedded (or the base href)
+     */
+    protected $currentUri;
+
+    /**
+     * Constructor.
+     *
+     * @param \DOMElement $node       A \DOMElement instance
+     * @param string      $currentUri The URI of the page where the link is embedded (or the base href)
+     * @param string      $method     The method to use for the link (get by default)
+     *
+     * @throws \InvalidArgumentException if the node is not a link
+     *
+     * @api
+     */
+    public function __construct(\DOMElement $node, $currentUri, $method = 'GET')
+    {
+        if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) {
+            throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri));
+        }
+
+        $this->setNode($node);
+        $this->method = $method ? strtoupper($method) : null;
+        $this->currentUri = $currentUri;
+    }
+
+    /**
+     * Gets the node associated with this link.
+     *
+     * @return \DOMElement A \DOMElement instance
+     */
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    /**
+     * Gets the method associated with this link.
+     *
+     * @return string The method
+     *
+     * @api
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * Gets the URI associated with this link.
+     *
+     * @return string The URI
+     *
+     * @api
+     */
+    public function getUri()
+    {
+        $uri = trim($this->getRawUri());
+
+        // absolute URL?
+        if (null !== parse_url($uri, PHP_URL_SCHEME)) {
+            return $uri;
+        }
+
+        // empty URI
+        if (!$uri) {
+            return $this->currentUri;
+        }
+
+        // an anchor
+        if ('#' === $uri[0]) {
+            return $this->cleanupAnchor($this->currentUri).$uri;
+        }
+
+        $baseUri = $this->cleanupUri($this->currentUri);
+
+        if ('?' === $uri[0]) {
+            return $baseUri.$uri;
+        }
+
+        // absolute URL with relative schema
+        if (0 === strpos($uri, '//')) {
+            return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri;
+        }
+
+        $baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri);
+
+        // absolute path
+        if ('/' === $uri[0]) {
+            return $baseUri.$uri;
+        }
+
+        // relative path
+        $path = parse_url(substr($this->currentUri, strlen($baseUri)), PHP_URL_PATH);
+        $path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);
+
+        return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path;
+    }
+
+    /**
+     * Returns raw URI data.
+     *
+     * @return string
+     */
+    protected function getRawUri()
+    {
+        return $this->node->getAttribute('href');
+    }
+
+    /**
+     * Returns the canonicalized URI path (see RFC 3986, section 5.2.4).
+     *
+     * @param string $path URI path
+     *
+     * @return string
+     */
+    protected function canonicalizePath($path)
+    {
+        if ('' === $path || '/' === $path) {
+            return $path;
+        }
+
+        if ('.' === substr($path, -1)) {
+            $path = $path.'/';
+        }
+
+        $output = array();
+
+        foreach (explode('/', $path) as $segment) {
+            if ('..' === $segment) {
+                array_pop($output);
+            } elseif ('.' !== $segment) {
+                array_push($output, $segment);
+            }
+        }
+
+        return implode('/', $output);
+    }
+
+    /**
+     * Sets current \DOMElement instance.
+     *
+     * @param \DOMElement $node A \DOMElement instance
+     *
+     * @throws \LogicException If given node is not an anchor
+     */
+    protected function setNode(\DOMElement $node)
+    {
+        if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) {
+            throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName));
+        }
+
+        $this->node = $node;
+    }
+
+    /**
+     * Removes the query string and the anchor from the given uri.
+     *
+     * @param string $uri The uri to clean
+     *
+     * @return string
+     */
+    private function cleanupUri($uri)
+    {
+        return $this->cleanupQuery($this->cleanupAnchor($uri));
+    }
+
+    /**
+     * Remove the query string from the uri.
+     *
+     * @param string $uri
+     *
+     * @return string
+     */
+    private function cleanupQuery($uri)
+    {
+        if (false !== $pos = strpos($uri, '?')) {
+            return substr($uri, 0, $pos);
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Remove the anchor from the uri.
+     *
+     * @param string $uri
+     *
+     * @return string
+     */
+    private function cleanupAnchor($uri)
+    {
+        if (false !== $pos = strpos($uri, '#')) {
+            return substr($uri, 0, $pos);
+        }
+
+        return $uri;
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md
new file mode 100644
index 0000000..646573f
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md
@@ -0,0 +1,36 @@
+DomCrawler Component
+====================
+
+DomCrawler eases DOM navigation for HTML and XML documents.
+
+If you are familiar with jQuery, DomCrawler is a PHP equivalent:
+
+```php
+use Symfony\Component\DomCrawler\Crawler;
+
+$crawler = new Crawler();
+$crawler->addContent('<html><body><p>Hello World!</p></body></html>');
+
+print $crawler->filterXPath('descendant-or-self::body/p')->text();
+```
+
+If you are also using the CssSelector component, you can use CSS Selectors
+instead of XPath expressions:
+
+```php
+use Symfony\Component\DomCrawler\Crawler;
+
+$crawler = new Crawler();
+$crawler->addContent('<html><body><p>Hello World!</p></body></html>');
+
+print $crawler->filter('body > p')->text();
+```
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/DomCrawler/
+    $ composer.phar install
+    $ phpunit
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
new file mode 100644
index 0000000..82045a1
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
@@ -0,0 +1,1072 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests;
+
+use Symfony\Component\CssSelector\CssSelector;
+use Symfony\Component\DomCrawler\Crawler;
+
+class CrawlerTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConstructor()
+    {
+        $crawler = new Crawler();
+        $this->assertCount(0, $crawler, '__construct() returns an empty crawler');
+
+        $crawler = new Crawler(new \DOMNode());
+        $this->assertCount(1, $crawler, '__construct() takes a node as a first argument');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::add
+     */
+    public function testAdd()
+    {
+        $crawler = new Crawler();
+        $crawler->add($this->createDomDocument());
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMDocument');
+
+        $crawler = new Crawler();
+        $crawler->add($this->createNodeList());
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList');
+
+        foreach ($this->createNodeList() as $node) {
+            $list[] = $node;
+        }
+        $crawler = new Crawler();
+        $crawler->add($list);
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from an array of nodes');
+
+        $crawler = new Crawler();
+        $crawler->add($this->createNodeList()->item(0));
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMElement');
+
+        $crawler = new Crawler();
+        $crawler->add('<html><body>Foo</body></html>');
+        $this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testAddInvalidNode()
+    {
+        $crawler = new Crawler();
+        $crawler->add(1);
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContent()
+    {
+        $crawler = new Crawler();
+        $crawler->addHtmlContent('<html><div class="foo"></html>', 'UTF-8');
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addHtmlContent() adds nodes from an HTML string');
+
+        $crawler->addHtmlContent('<html><head><base href="http://symfony.com"></head><a href="/contact"></a></html>', 'UTF-8');
+
+        $this->assertEquals('http://symfony.com', $crawler->filterXPath('//base')->attr('href'), '->addHtmlContent() adds nodes from an HTML string');
+        $this->assertEquals('http://symfony.com/contact', $crawler->filterXPath('//a')->link()->getUri(), '->addHtmlContent() adds nodes from an HTML string');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentCharset()
+    {
+        $crawler = new Crawler();
+        $crawler->addHtmlContent('<html><div class="foo">Tiáº¿ng Viá»‡t</html>', 'UTF-8');
+
+        $this->assertEquals('Tiáº¿ng Viá»‡t', $crawler->filterXPath('//div')->text());
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentInvalidBaseTag()
+    {
+        $crawler = new Crawler(null, 'http://symfony.com');
+
+        $crawler->addHtmlContent('<html><head><base target="_top"></head><a href="/contact"></a></html>', 'UTF-8');
+
+        $this->assertEquals('http://symfony.com/contact', current($crawler->filterXPath('//a')->links())->getUri(), '->addHtmlContent() correctly handles a non-existent base tag href attribute');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentUnsupportedCharset()
+    {
+        $crawler = new Crawler();
+        $crawler->addHtmlContent(file_get_contents(__DIR__.'/Fixtures/windows-1250.html'), 'Windows-1250');
+
+        $this->assertEquals('Å½Å¥ÄÃ½Å¯', $crawler->filterXPath('//p')->text());
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentCharsetGbk()
+    {
+        $crawler = new Crawler();
+        //gbk encode of <html><p>ä¸­æ–‡</p></html>
+        $crawler->addHtmlContent(base64_decode('PGh0bWw+PHA+1tDOxDwvcD48L2h0bWw+'), 'gbk');
+
+        $this->assertEquals('ä¸­æ–‡', $crawler->filterXPath('//p')->text());
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentWithErrors()
+    {
+        $internalErrors = libxml_use_internal_errors(true);
+
+        $crawler = new Crawler();
+        $crawler->addHtmlContent(<<<EOF
+<!DOCTYPE html>
+<html>
+    <head>
+    </head>
+    <body>
+        <nav><a href="#"><a href="#"></nav>
+    </body>
+</html>
+EOF
+        , 'UTF-8');
+
+        $errors = libxml_get_errors();
+        $this->assertCount(1, $errors);
+        $this->assertEquals("Tag nav invalid\n", $errors[0]->message);
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internalErrors);
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent
+     */
+    public function testAddXmlContent()
+    {
+        $crawler = new Crawler();
+        $crawler->addXmlContent('<html><div class="foo"></div></html>', 'UTF-8');
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addXmlContent() adds nodes from an XML string');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent
+     */
+    public function testAddXmlContentCharset()
+    {
+        $crawler = new Crawler();
+        $crawler->addXmlContent('<html><div class="foo">Tiáº¿ng Viá»‡t</div></html>', 'UTF-8');
+
+        $this->assertEquals('Tiáº¿ng Viá»‡t', $crawler->filterXPath('//div')->text());
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent
+     */
+    public function testAddXmlContentWithErrors()
+    {
+        $internalErrors = libxml_use_internal_errors(true);
+
+        $crawler = new Crawler();
+        $crawler->addXmlContent(<<<EOF
+<!DOCTYPE html>
+<html>
+    <head>
+    </head>
+    <body>
+        <nav><a href="#"><a href="#"></nav>
+    </body>
+</html>
+EOF
+        , 'UTF-8');
+
+        $this->assertTrue(count(libxml_get_errors()) > 1);
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internalErrors);
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addContent
+     */
+    public function testAddContent()
+    {
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></html>', 'text/html; charset=UTF-8');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></html>', 'text/html; charset=UTF-8; dir=RTL');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string with extended content type');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></html>');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() uses text/html as the default type');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></div></html>', 'text/xml; charset=UTF-8');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></div></html>', 'text/xml');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string');
+
+        $crawler = new Crawler();
+        $crawler->addContent('foo bar', 'text/plain');
+        $this->assertCount(0, $crawler, '->addContent() does nothing if the type is not (x|ht)ml');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><span>ä¸­æ–‡</span></html>');
+        $this->assertEquals('ä¸­æ–‡', $crawler->filterXPath('//span')->text(), '->addContent() guess wrong charset');
+
+        $crawler = new Crawler();
+        $crawler->addContent(mb_convert_encoding('<html><head><meta charset="Shift_JIS"></head><body>æ—¥æœ¬èªž</body></html>', 'SJIS', 'UTF-8'));
+        $this->assertEquals('æ—¥æœ¬èªž', $crawler->filterXPath('//body')->text(), '->addContent() can recognize "Shift_JIS" in html5 meta charset tag');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addDocument
+     */
+    public function testAddDocument()
+    {
+        $crawler = new Crawler();
+        $crawler->addDocument($this->createDomDocument());
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addNodeList
+     */
+    public function testAddNodeList()
+    {
+        $crawler = new Crawler();
+        $crawler->addNodeList($this->createNodeList());
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addNodes
+     */
+    public function testAddNodes()
+    {
+        foreach ($this->createNodeList() as $node) {
+            $list[] = $node;
+        }
+
+        $crawler = new Crawler();
+        $crawler->addNodes($list);
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodes() adds nodes from an array of nodes');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addNode
+     */
+    public function testAddNode()
+    {
+        $crawler = new Crawler();
+        $crawler->addNode($this->createNodeList()->item(0));
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNode() adds nodes from a \DOMElement');
+    }
+
+    public function testClear()
+    {
+        $crawler = new Crawler(new \DOMNode());
+        $crawler->clear();
+        $this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler');
+    }
+
+    public function testEq()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li');
+        $this->assertNotSame($crawler, $crawler->eq(0), '->eq() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->eq() returns a new instance of a crawler');
+
+        $this->assertEquals('Two', $crawler->eq(1)->text(), '->eq() returns the nth node of the list');
+        $this->assertCount(0, $crawler->eq(100), '->eq() returns an empty crawler if the nth node does not exist');
+    }
+
+    public function testEach()
+    {
+        $data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) {
+            return $i.'-'.$node->text();
+        });
+
+        $this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list');
+    }
+
+    public function testSlice()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
+        $this->assertNotSame($crawler->slice(), $crawler, '->slice() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler->slice(), '->slice() returns a new instance of a crawler');
+
+        $this->assertCount(3, $crawler->slice(), '->slice() does not slice the nodes in the list if any param is entered');
+        $this->assertCount(1, $crawler->slice(1, 1), '->slice() slices the nodes in the list');
+    }
+
+    public function testReduce()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
+        $nodes = $crawler->reduce(function ($node, $i) {
+            return $i == 1 ? false : true;
+        });
+        $this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler');
+
+        $this->assertCount(2, $nodes, '->reduce() filters the nodes in the list');
+    }
+
+    public function testAttr()
+    {
+        $this->assertEquals('first', $this->createTestCrawler()->filterXPath('//li')->attr('class'), '->attr() returns the attribute of the first element of the node list');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->attr('class');
+            $this->fail('->attr() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->attr() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testMissingAttrValueIsNull()
+    {
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div non-empty-attr="sample value" empty-attr=""></div></html>', 'text/html; charset=UTF-8');
+        $div = $crawler->filterXPath('//div');
+
+        $this->assertEquals('sample value', $div->attr('non-empty-attr'), '->attr() reads non-empty attributes correctly');
+        $this->assertEquals('', $div->attr('empty-attr'), '->attr() reads empty attributes correctly');
+        $this->assertNull($div->attr('missing-attr'), '->attr() reads missing attributes correctly');
+    }
+
+    public function testNodeName()
+    {
+        $this->assertEquals('li', $this->createTestCrawler()->filterXPath('//li')->nodeName(), '->nodeName() returns the node name of the first element of the node list');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->nodeName();
+            $this->fail('->nodeName() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->nodeName() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testText()
+    {
+        $this->assertEquals('One', $this->createTestCrawler()->filterXPath('//li')->text(), '->text() returns the node value of the first element of the node list');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->text();
+            $this->fail('->text() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->text() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testHtml()
+    {
+        $this->assertEquals('<img alt="Bar">', $this->createTestCrawler()->filterXPath('//a[5]')->html());
+        $this->assertEquals('<input type="text" value="TextValue" name="TextName"><input type="submit" value="FooValue" name="FooName" id="FooId"><input type="button" value="BarValue" name="BarName" id="BarId"><button value="ButtonValue" name="ButtonName" id="ButtonId"></button>', trim($this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html()));
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->html();
+            $this->fail('->html() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->html() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testExtract()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
+
+        $this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list');
+        $this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list');
+
+        $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty');
+    }
+
+    public function testFilterXpathComplexQueries()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//body');
+
+        $this->assertCount(0, $crawler->filterXPath('/input'));
+        $this->assertCount(0, $crawler->filterXPath('/body'));
+        $this->assertCount(1, $crawler->filterXPath('/_root/body'));
+        $this->assertCount(1, $crawler->filterXPath('./body'));
+        $this->assertCount(1, $crawler->filterXPath('.//body'));
+        $this->assertCount(5, $crawler->filterXPath('.//input'));
+        $this->assertCount(4, $crawler->filterXPath('//form')->filterXPath('//button | //input'));
+        $this->assertCount(1, $crawler->filterXPath('body'));
+        $this->assertCount(6, $crawler->filterXPath('//button | //input'));
+        $this->assertCount(1, $crawler->filterXPath('//body'));
+        $this->assertCount(1, $crawler->filterXPath('descendant-or-self::body'));
+        $this->assertCount(1, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('./div'), 'A child selection finds only the current div');
+        $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('descendant::div'), 'A descendant selector matches the current div and its child');
+        $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('//div'), 'A descendant selector matches the current div and its child');
+        $this->assertCount(5, $crawler->filterXPath('(//a | //div)//img'));
+        $this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)'));
+        $this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )'));
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::filterXPath
+     */
+    public function testFilterXPath()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->filterXPath('//li'), '->filterXPath() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler');
+
+        $crawler = $this->createTestCrawler()->filterXPath('//ul');
+        $this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression');
+
+        $crawler = $this->createTestCrawler();
+        $this->assertCount(3, $crawler->filterXPath('//body')->filterXPath('//button')->parents(), '->filterXpath() preserves parents when chained');
+    }
+
+    public function testFilterXPathWithDefaultNamespace()
+    {
+        $crawler = $this->createTestXmlCrawler()->filterXPath('//default:entry/default:id');
+        $this->assertCount(1, $crawler, '->filterXPath() automatically registers a namespace');
+        $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text());
+    }
+
+    public function testFilterXPathWithCustomDefaultNamespace()
+    {
+        $crawler = $this->createTestXmlCrawler();
+        $crawler->setDefaultNamespacePrefix('x');
+        $crawler = $crawler->filterXPath('//x:entry/x:id');
+
+        $this->assertCount(1, $crawler, '->filterXPath() lets to override the default namespace prefix');
+        $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text());
+    }
+
+    public function testFilterXPathWithNamespace()
+    {
+        $crawler = $this->createTestXmlCrawler()->filterXPath('//yt:accessControl');
+        $this->assertCount(2, $crawler, '->filterXPath() automatically registers a namespace');
+    }
+
+    public function testFilterXPathWithMultipleNamespaces()
+    {
+        $crawler = $this->createTestXmlCrawler()->filterXPath('//media:group/yt:aspectRatio');
+        $this->assertCount(1, $crawler, '->filterXPath() automatically registers multiple namespaces');
+        $this->assertSame('widescreen', $crawler->text());
+    }
+
+    public function testFilterXPathWithManuallyRegisteredNamespace()
+    {
+        $crawler = $this->createTestXmlCrawler();
+        $crawler->registerNamespace('m', 'http://search.yahoo.com/mrss/');
+
+        $crawler = $crawler->filterXPath('//m:group/yt:aspectRatio');
+        $this->assertCount(1, $crawler, '->filterXPath() uses manually registered namespace');
+        $this->assertSame('widescreen', $crawler->text());
+    }
+
+    public function testFilterXPathWithAnUrl()
+    {
+        $crawler = $this->createTestXmlCrawler();
+
+        $crawler = $crawler->filterXPath('//media:category[@scheme="http://gdata.youtube.com/schemas/2007/categories.cat"]');
+        $this->assertCount(1, $crawler);
+        $this->assertSame('Music', $crawler->text());
+    }
+
+    public function testFilterXPathWithFakeRoot()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertCount(0, $crawler->filterXPath('.'), '->filterXPath() returns an empty result if the XPath references the fake root node');
+        $this->assertCount(0, $crawler->filterXPath('/_root'), '->filterXPath() returns an empty result if the XPath references the fake root node');
+        $this->assertCount(0, $crawler->filterXPath('self::*'), '->filterXPath() returns an empty result if the XPath references the fake root node');
+        $this->assertCount(0, $crawler->filterXPath('self::_root'), '->filterXPath() returns an empty result if the XPath references the fake root node');
+    }
+
+    public function testFilterXPathWithAncestorAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//form');
+
+        $this->assertCount(0, $crawler->filterXPath('ancestor::*'), 'The fake root node has no ancestor nodes');
+    }
+
+    public function testFilterXPathWithAncestorOrSelfAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//form');
+
+        $this->assertCount(0, $crawler->filterXPath('ancestor-or-self::*'), 'The fake root node has no ancestor nodes');
+    }
+
+    public function testFilterXPathWithAttributeAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//form');
+
+        $this->assertCount(0, $crawler->filterXPath('attribute::*'), 'The fake root node has no attribute nodes');
+    }
+
+    public function testFilterXPathWithAttributeAxisAfterElementAxis()
+    {
+        $this->assertCount(3, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis');
+    }
+
+    public function testFilterXPathWithChildAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]');
+
+        $this->assertCount(1, $crawler->filterXPath('child::div'), 'A child selection finds only the current div');
+    }
+
+    public function testFilterXPathWithFollowingAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//a');
+
+        $this->assertCount(0, $crawler->filterXPath('following::div'), 'The fake root node has no following nodes');
+    }
+
+    public function testFilterXPathWithFollowingSiblingAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//a');
+
+        $this->assertCount(0, $crawler->filterXPath('following-sibling::div'), 'The fake root node has no following nodes');
+    }
+
+    public function testFilterXPathWithNamespaceAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//button');
+
+        $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'The fake root node has no namespace nodes');
+    }
+
+    public function testFilterXPathWithNamespaceAxisAfterElementAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]/namespace::*');
+
+        $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'Namespace axes cannot be requested');
+    }
+
+    public function testFilterXPathWithParentAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//button');
+
+        $this->assertCount(0, $crawler->filterXPath('parent::*'), 'The fake root node has no parent nodes');
+    }
+
+    public function testFilterXPathWithPrecedingAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//form');
+
+        $this->assertCount(0, $crawler->filterXPath('preceding::*'), 'The fake root node has no preceding nodes');
+    }
+
+    public function testFilterXPathWithPrecedingSiblingAxis()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//form');
+
+        $this->assertCount(0, $crawler->filterXPath('preceding-sibling::*'), 'The fake root node has no preceding nodes');
+    }
+
+    public function testFilterXPathWithSelfAxes()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//a');
+
+        $this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name');
+        $this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name');
+        $this->assertCount(9, $crawler->filterXPath('self::*/a'));
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::filter
+     */
+    public function testFilter()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->filter('li'), '->filter() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filter() returns a new instance of a crawler');
+
+        $crawler = $this->createTestCrawler()->filter('ul');
+
+        $this->assertCount(6, $crawler->filter('li'), '->filter() filters the node list with the CSS selector');
+    }
+
+    public function testFilterWithDefaultNamespace()
+    {
+        $crawler = $this->createTestXmlCrawler()->filter('default|entry default|id');
+        $this->assertCount(1, $crawler, '->filter() automatically registers namespaces');
+        $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text());
+    }
+
+    public function testFilterWithNamespace()
+    {
+        CssSelector::disableHtmlExtension();
+
+        $crawler = $this->createTestXmlCrawler()->filter('yt|accessControl');
+        $this->assertCount(2, $crawler, '->filter() automatically registers namespaces');
+    }
+
+    public function testFilterWithMultipleNamespaces()
+    {
+        CssSelector::disableHtmlExtension();
+
+        $crawler = $this->createTestXmlCrawler()->filter('media|group yt|aspectRatio');
+        $this->assertCount(1, $crawler, '->filter() automatically registers namespaces');
+        $this->assertSame('widescreen', $crawler->text());
+    }
+
+    public function testFilterWithDefaultNamespaceOnly()
+    {
+        $crawler = new Crawler('<?xml version="1.0" encoding="UTF-8"?>
+            <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+                <url>
+                    <loc>http://localhost/foo</loc>
+                    <changefreq>weekly</changefreq>
+                    <priority>0.5</priority>
+                    <lastmod>2012-11-16</lastmod>
+               </url>
+               <url>
+                    <loc>http://localhost/bar</loc>
+                    <changefreq>weekly</changefreq>
+                    <priority>0.5</priority>
+                    <lastmod>2012-11-16</lastmod>
+                </url>
+            </urlset>
+        ');
+
+        $this->assertEquals(2, $crawler->filter('url')->count());
+    }
+
+    public function testSelectLink()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->selectLink('Foo'), '->selectLink() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectLink() returns a new instance of a crawler');
+
+        $this->assertCount(1, $crawler->selectLink('Fabien\'s Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(1, $crawler->selectLink('Fabien\'s Bar'), '->selectLink() selects links by the alt attribute of a clickable image');
+
+        $this->assertCount(2, $crawler->selectLink('Fabien"s Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(2, $crawler->selectLink('Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image');
+
+        $this->assertCount(1, $crawler->selectLink('\' Fabien"s Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(1, $crawler->selectLink('\' Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image');
+
+        $this->assertCount(4, $crawler->selectLink('Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(4, $crawler->selectLink('Bar'), '->selectLink() selects links by the node values');
+    }
+
+    public function testSelectButton()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->selectButton('FooValue'), '->selectButton() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectButton() returns a new instance of a crawler');
+
+        $this->assertEquals(1, $crawler->selectButton('FooValue')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('FooName')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('FooId')->count(), '->selectButton() selects buttons');
+
+        $this->assertEquals(1, $crawler->selectButton('BarValue')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('BarName')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons');
+
+        $this->assertEquals(1, $crawler->selectButton('FooBarValue')->count(), '->selectButton() selects buttons with form attribute too');
+        $this->assertEquals(1, $crawler->selectButton('FooBarName')->count(), '->selectButton() selects buttons with form attribute too');
+    }
+
+    public function testSelectButtonWithSingleQuotesInNameAttribute()
+    {
+        $html = <<<HTML
+<!DOCTYPE html>
+<html lang="en">
+<body>
+    <div id="action">
+        <a href="/index.php?r=site/login">Login</a>
+    </div>
+    <form id="login-form" action="/index.php?r=site/login" method="post">
+        <button type="submit" name="Click 'Here'">Submit</button>
+    </form>
+</body>
+</html>
+HTML;
+
+        $crawler = new Crawler($html);
+
+        $this->assertCount(1, $crawler->selectButton('Click \'Here\''));
+    }
+
+    public function testSelectButtonWithDoubleQuotesInNameAttribute()
+    {
+        $html = <<<HTML
+<!DOCTYPE html>
+<html lang="en">
+<body>
+    <div id="action">
+        <a href="/index.php?r=site/login">Login</a>
+    </div>
+    <form id="login-form" action="/index.php?r=site/login" method="post">
+        <button type="submit" name='Click "Here"'>Submit</button>
+    </form>
+</body>
+</html>
+HTML;
+
+        $crawler = new Crawler($html);
+
+        $this->assertCount(1, $crawler->selectButton('Click "Here"'));
+    }
+
+    public function testLink()
+    {
+        $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance');
+
+        $this->assertEquals('POST', $crawler->link('post')->getMethod(), '->link() takes a method as its argument');
+
+        $crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('GetLink');
+        $this->assertEquals('http://example.com/bar?get=param', $crawler->link()->getUri(), '->link() returns a Link instance');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->link();
+            $this->fail('->link() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->link() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testSelectLinkAndLinkFiltered()
+    {
+        $html = <<<HTML
+<!DOCTYPE html>
+<html lang="en">
+<body>
+    <div id="action">
+        <a href="/index.php?r=site/login">Login</a>
+    </div>
+    <form id="login-form" action="/index.php?r=site/login" method="post">
+        <button type="submit">Submit</button>
+    </form>
+</body>
+</html>
+HTML;
+
+        $crawler = new Crawler($html);
+        $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'login-form']");
+
+        $this->assertCount(0, $filtered->selectLink('Login'));
+        $this->assertCount(1, $filtered->selectButton('Submit'));
+
+        $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'action']");
+
+        $this->assertCount(1, $filtered->selectLink('Login'));
+        $this->assertCount(0, $filtered->selectButton('Submit'));
+
+        $this->assertCount(1, $crawler->selectLink('Login')->selectLink('Login'));
+        $this->assertCount(1, $crawler->selectButton('Submit')->selectButton('Submit'));
+    }
+
+    public function testChaining()
+    {
+        $crawler = new Crawler('<div name="a"><div name="b"><div name="c"></div></div></div>');
+
+        $this->assertEquals('a', $crawler->filterXPath('//div')->filterXPath('div')->filterXPath('div')->attr('name'));
+    }
+
+    public function testLinks()
+    {
+        $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
+        $this->assertInternalType('array', $crawler->links(), '->links() returns an array');
+
+        $this->assertCount(4, $crawler->links(), '->links() returns an array');
+        $links = $crawler->links();
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances');
+
+        $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty');
+    }
+
+    public function testForm()
+    {
+        $testCrawler = $this->createTestCrawler('http://example.com/bar/');
+        $crawler = $testCrawler->selectButton('FooValue');
+        $crawler2 = $testCrawler->selectButton('FooBarValue');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler2->form(), '->form() returns a Form instance');
+
+        $this->assertEquals($crawler->form()->getFormNode()->getAttribute('id'), $crawler2->form()->getFormNode()->getAttribute('id'), '->form() works on elements with form attribute');
+
+        $this->assertEquals(array('FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument');
+        $this->assertEquals(array('FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form()->getValues(), '->getValues() returns correct form values');
+        $this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->getValues() returns correct form values');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->form();
+            $this->fail('->form() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->form() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testLast()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
+        $this->assertNotSame($crawler, $crawler->last(), '->last() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->last() returns a new instance of a crawler');
+
+        $this->assertEquals('Three', $crawler->last()->text());
+    }
+
+    public function testFirst()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li');
+        $this->assertNotSame($crawler, $crawler->first(), '->first() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->first() returns a new instance of a crawler');
+
+        $this->assertEquals('One', $crawler->first()->text());
+    }
+
+    public function testSiblings()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1);
+        $this->assertNotSame($crawler, $crawler->siblings(), '->siblings() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->siblings() returns a new instance of a crawler');
+
+        $nodes = $crawler->siblings();
+        $this->assertEquals(2, $nodes->count());
+        $this->assertEquals('One', $nodes->eq(0)->text());
+        $this->assertEquals('Three', $nodes->eq(1)->text());
+
+        $nodes = $this->createTestCrawler()->filterXPath('//li')->eq(0)->siblings();
+        $this->assertEquals(2, $nodes->count());
+        $this->assertEquals('Two', $nodes->eq(0)->text());
+        $this->assertEquals('Three', $nodes->eq(1)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->siblings();
+            $this->fail('->siblings() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->siblings() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testNextAll()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1);
+        $this->assertNotSame($crawler, $crawler->nextAll(), '->nextAll() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->nextAll() returns a new instance of a crawler');
+
+        $nodes = $crawler->nextAll();
+        $this->assertEquals(1, $nodes->count());
+        $this->assertEquals('Three', $nodes->eq(0)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->nextAll();
+            $this->fail('->nextAll() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->nextAll() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testPreviousAll()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(2);
+        $this->assertNotSame($crawler, $crawler->previousAll(), '->previousAll() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->previousAll() returns a new instance of a crawler');
+
+        $nodes = $crawler->previousAll();
+        $this->assertEquals(2, $nodes->count());
+        $this->assertEquals('Two', $nodes->eq(0)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->previousAll();
+            $this->fail('->previousAll() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->previousAll() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testChildren()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul');
+        $this->assertNotSame($crawler, $crawler->children(), '->children() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->children() returns a new instance of a crawler');
+
+        $nodes = $crawler->children();
+        $this->assertEquals(3, $nodes->count());
+        $this->assertEquals('One', $nodes->eq(0)->text());
+        $this->assertEquals('Two', $nodes->eq(1)->text());
+        $this->assertEquals('Three', $nodes->eq(2)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->children();
+            $this->fail('->children() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty');
+        }
+
+        try {
+            $crawler = new Crawler('<p></p>');
+            $crawler->filter('p')->children();
+            $this->assertTrue(true, '->children() does not trigger a notice if the node has no children');
+        } catch (\PHPUnit_Framework_Error_Notice $e) {
+            $this->fail('->children() does not trigger a notice if the node has no children');
+        }
+    }
+
+    public function testParents()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li[1]');
+        $this->assertNotSame($crawler, $crawler->parents(), '->parents() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->parents() returns a new instance of a crawler');
+
+        $nodes = $crawler->parents();
+        $this->assertEquals(3, $nodes->count());
+
+        $nodes = $this->createTestCrawler()->filterXPath('//html')->parents();
+        $this->assertEquals(0, $nodes->count());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->parents();
+            $this->fail('->parents() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->parents() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    /**
+     * @dataProvider getBaseTagData
+     */
+    public function testBaseTag($baseValue, $linkValue, $expectedUri, $currentUri = null, $description = null)
+    {
+        $crawler = new Crawler('<html><base href="'.$baseValue.'"><a href="'.$linkValue.'"></a></html>', $currentUri);
+        $this->assertEquals($expectedUri, $crawler->filterXPath('//a')->link()->getUri(), $description);
+    }
+
+    public function getBaseTagData()
+    {
+        return array(
+            array('http://base.com', 'link', 'http://base.com/link'),
+            array('//base.com', 'link', 'https://base.com/link', 'https://domain.com', '<base> tag can use a schema-less URL'),
+            array('path/', 'link', 'https://domain.com/path/link', 'https://domain.com', '<base> tag can set a path'),
+            array('http://base.com', '#', 'http://base.com#', 'http://domain.com/path/link', '<base> tag does work with links to an anchor'),
+            array('http://base.com', '', 'http://base.com', 'http://domain.com/path/link', '<base> tag does work with empty links'),
+        );
+    }
+
+    /**
+     * @dataProvider getBaseTagWithFormData
+     */
+    public function testBaseTagWithForm($baseValue, $actionValue, $expectedUri, $currentUri = null, $description = null)
+    {
+        $crawler = new Crawler('<html><base href="'.$baseValue.'"><form method="post" action="'.$actionValue.'"><button type="submit" name="submit"/></form></html>', $currentUri);
+        $this->assertEquals($expectedUri, $crawler->filterXPath('//button')->form()->getUri(), $description);
+    }
+
+    public function getBaseTagWithFormData()
+    {
+        return array(
+            array('/basepath', '/registration', 'http://domain.com/registration', 'http://domain.com/registration', '<base> tag does work with a path and form action'),
+            array('/basepath', '', 'http://domain.com/registration', 'http://domain.com/registration', '<base> tag does work with a path and empty form action'),
+            array('http://base.com', '', 'http://domain.com/path/form', 'http://domain.com/path/form', '<base> tag does work with a URL and an empty form action'),
+        );
+    }
+
+    public function createTestCrawler($uri = null)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <body>
+                    <a href="foo">Foo</a>
+                    <a href="/foo">   Fabien\'s Foo   </a>
+                    <a href="/foo">Fabien"s Foo</a>
+                    <a href="/foo">\' Fabien"s Foo</a>
+
+                    <a href="/bar"><img alt="Bar"/></a>
+                    <a href="/bar"><img alt="   Fabien\'s Bar   "/></a>
+                    <a href="/bar"><img alt="Fabien&quot;s Bar"/></a>
+                    <a href="/bar"><img alt="\' Fabien&quot;s Bar"/></a>
+
+                    <a href="?get=param">GetLink</a>
+
+                    <form action="foo" id="FooFormId">
+                        <input type="text" value="TextValue" name="TextName" />
+                        <input type="submit" value="FooValue" name="FooName" id="FooId" />
+                        <input type="button" value="BarValue" name="BarName" id="BarId" />
+                        <button value="ButtonValue" name="ButtonName" id="ButtonId" />
+                    </form>
+
+                    <input type="submit" value="FooBarValue" name="FooBarName" form="FooFormId" />
+                    <input type="text" value="FooTextValue" name="FooTextName" form="FooFormId" />
+
+                    <ul class="first">
+                        <li class="first">One</li>
+                        <li>Two</li>
+                        <li>Three</li>
+                    </ul>
+                    <ul>
+                        <li>One Bis</li>
+                        <li>Two Bis</li>
+                        <li>Three Bis</li>
+                    </ul>
+                    <div id="parent">
+                        <div id="child"></div>
+                        <div id="child2" xmlns:foo="http://example.com"></div>
+                    </div>
+                    <div id="sibling"><img /></div>
+                </body>
+            </html>
+        ');
+
+        return new Crawler($dom, $uri);
+    }
+
+    protected function createTestXmlCrawler($uri = null)
+    {
+        $xml = '<?xml version="1.0" encoding="UTF-8"?>
+            <entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007">
+                <id>tag:youtube.com,2008:video:kgZRZmEc9j4</id>
+                <yt:accessControl action="comment" permission="allowed"/>
+                <yt:accessControl action="videoRespond" permission="moderated"/>
+                <media:group>
+                    <media:title type="plain">Chordates - CrashCourse Biology #24</media:title>
+                    <yt:aspectRatio>widescreen</yt:aspectRatio>
+                </media:group>
+                <media:category label="Music" scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Music</media:category>
+            </entry>';
+
+        return new Crawler($xml, $uri);
+    }
+
+    protected function createDomDocument()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadXML('<html><div class="foo"></div></html>');
+
+        return $dom;
+    }
+
+    protected function createNodeList()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadXML('<html><div class="foo"></div></html>');
+        $domxpath = new \DOMXPath($dom);
+
+        return $domxpath->query('//div');
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php
new file mode 100644
index 0000000..9b31945
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php
@@ -0,0 +1,388 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\ChoiceFormField;
+
+class ChoiceFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('textarea', '');
+        try {
+            $field = new ChoiceFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not an input or a select');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input or a select');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'text'));
+        try {
+            $field = new ChoiceFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio');
+        }
+    }
+
+    public function testGetType()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('radio', $field->getType(), '->getType() returns radio for radio buttons');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('checkbox', $field->getType(), '->getType() returns radio for a checkbox');
+
+        $node = $this->createNode('select', '');
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('select', $field->getType(), '->getType() returns radio for a select');
+    }
+
+    public function testIsMultiple()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for checkboxes');
+
+        $node = $this->createNode('select', '');
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for selects without the multiple attribute');
+
+        $node = $this->createNode('select', '', array('multiple' => 'multiple'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->isMultiple(), '->isMultiple() returns true for selects with the multiple attribute');
+
+        $node = $this->createNode('select', '', array('multiple' => ''));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->isMultiple(), '->isMultiple() returns true for selects with an empty multiple attribute');
+    }
+
+    public function testSelects()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true for selects');
+        $this->assertEquals('foo', $field->getValue(), '->getValue() returns the first option if none are selected');
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false when no multiple attribute is defined');
+
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => true));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('bar', $field->getValue(), '->getValue() returns the selected option');
+
+        $field->setValue('foo');
+        $this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected option');
+
+        try {
+            $field->setValue('foobar');
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the selected options');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the selected options');
+        }
+
+        try {
+            $field->setValue(array('foobar'));
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is an array');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is an array');
+        }
+    }
+
+    public function testSelectWithEmptyBooleanAttribute()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => true), array(), '');
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('bar', $field->getValue());
+    }
+
+    public function testMultipleSelects()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals(array(), $field->getValue(), '->setValue() returns an empty array if multiple is true and no option is selected');
+
+        $field->setValue('foo');
+        $this->assertEquals(array('foo'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
+
+        $field->setValue('bar');
+        $this->assertEquals(array('bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
+
+        $field->setValue(array('foo', 'bar'));
+        $this->assertEquals(array('foo', 'bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
+
+        $node = $this->createSelectNode(array('foo' => true, 'bar' => true), array('multiple' => 'multiple'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals(array('foo', 'bar'), $field->getValue(), '->getValue() returns the selected options');
+
+        try {
+            $field->setValue(array('foobar'));
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the options');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the options');
+        }
+    }
+
+    public function testRadioButtons()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar'));
+        $field->addChoice($node);
+
+        $this->assertFalse($field->hasValue(), '->hasValue() returns false when no radio button is selected');
+        $this->assertNull($field->getValue(), '->getValue() returns null if no radio button is selected');
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons');
+
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => 'checked'));
+        $field->addChoice($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected');
+        $this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+
+        $field->setValue('foo');
+        $this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected radio button');
+
+        try {
+            $field->setValue('foobar');
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values');
+        }
+    }
+
+    public function testRadioButtonsWithEmptyBooleanAttribute()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => ''));
+        $field->addChoice($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected');
+        $this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+    }
+
+    public function testRadioButtonIsDisabled()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo', 'disabled' => 'disabled'));
+        $field = new ChoiceFormField($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar'));
+        $field->addChoice($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'baz', 'disabled' => ''));
+        $field->addChoice($node);
+
+        $field->select('foo');
+        $this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+        $this->assertTrue($field->isDisabled());
+
+        $field->select('bar');
+        $this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+        $this->assertFalse($field->isDisabled());
+
+        $field->select('baz');
+        $this->assertEquals('baz', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+        $this->assertTrue($field->isDisabled());
+    }
+
+    public function testCheckboxes()
+    {
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->hasValue(), '->hasValue() returns false when the checkbox is not checked');
+        $this->assertNull($field->getValue(), '->getValue() returns null if the checkbox is not checked');
+        $this->assertFalse($field->isMultiple(), '->hasValue() returns false for checkboxes');
+        try {
+            $field->addChoice(new \DOMElement('input'));
+            $this->fail('->addChoice() throws a \LogicException for checkboxes');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException for checkboxes');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked');
+        $this->assertEquals('on', $field->getValue(), '->getValue() returns 1 if the checkbox is checked and has no value attribute');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute if the checkbox is checked');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $field->setValue(false);
+        $this->assertNull($field->getValue(), '->setValue() unchecks the checkbox is value is false');
+
+        $field->setValue(true);
+        $this->assertEquals('foo', $field->getValue(), '->setValue() checks the checkbox is value is true');
+
+        try {
+            $field->setValue('bar');
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute');
+        }
+    }
+
+    public function testCheckboxWithEmptyBooleanAttribute()
+    {
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo', 'checked' => ''));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked');
+        $this->assertEquals('foo', $field->getValue());
+    }
+
+    public function testTick()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+
+        try {
+            $field->tick();
+            $this->fail('->tick() throws a \LogicException for select boxes');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->tick() throws a \LogicException for select boxes');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name'));
+        $field = new ChoiceFormField($node);
+        $field->tick();
+        $this->assertEquals('on', $field->getValue(), '->tick() ticks checkboxes');
+    }
+
+    public function testUntick()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+
+        try {
+            $field->untick();
+            $this->fail('->untick() throws a \LogicException for select boxes');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->untick() throws a \LogicException for select boxes');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked'));
+        $field = new ChoiceFormField($node);
+        $field->untick();
+        $this->assertNull($field->getValue(), '->untick() unticks checkboxes');
+    }
+
+    public function testSelect()
+    {
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked'));
+        $field = new ChoiceFormField($node);
+        $field->select(true);
+        $this->assertEquals('on', $field->getValue(), '->select() changes the value of the field');
+        $field->select(false);
+        $this->assertNull($field->getValue(), '->select() changes the value of the field');
+
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+        $field->select('foo');
+        $this->assertEquals('foo', $field->getValue(), '->select() changes the selected option');
+    }
+
+    public function testOptionWithNoValue()
+    {
+        $node = $this->createSelectNodeWithEmptyOption(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+        $this->assertEquals('foo', $field->getValue());
+
+        $node = $this->createSelectNodeWithEmptyOption(array('foo' => false, 'bar' => true));
+        $field = new ChoiceFormField($node);
+        $this->assertEquals('bar', $field->getValue());
+        $field->select('foo');
+        $this->assertEquals('foo', $field->getValue(), '->select() changes the selected option');
+    }
+
+    public function testDisableValidation()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+        $field->disableValidation();
+        $field->setValue('foobar');
+        $this->assertEquals('foobar', $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.');
+
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple'));
+        $field = new ChoiceFormField($node);
+        $field->disableValidation();
+        $field->setValue(array('foobar'));
+        $this->assertEquals(array('foobar'), $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.');
+    }
+
+    protected function createSelectNode($options, $attributes = array(), $selectedAttrText = 'selected')
+    {
+        $document = new \DOMDocument();
+        $node = $document->createElement('select');
+
+        foreach ($attributes as $name => $value) {
+            $node->setAttribute($name, $value);
+        }
+        $node->setAttribute('name', 'name');
+
+        foreach ($options as $value => $selected) {
+            $option = $document->createElement('option', $value);
+            $option->setAttribute('value', $value);
+            if ($selected) {
+                $option->setAttribute('selected', $selectedAttrText);
+            }
+            $node->appendChild($option);
+        }
+
+        return $node;
+    }
+
+    protected function createSelectNodeWithEmptyOption($options, $attributes = array())
+    {
+        $document = new \DOMDocument();
+        $node = $document->createElement('select');
+
+        foreach ($attributes as $name => $value) {
+            $node->setAttribute($name, $value);
+        }
+        $node->setAttribute('name', 'name');
+
+        foreach ($options as $value => $selected) {
+            $option = $document->createElement('option', $value);
+            if ($selected) {
+                $option->setAttribute('selected', 'selected');
+            }
+            $node->appendChild($option);
+        }
+
+        return $node;
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php
new file mode 100644
index 0000000..3ce49a4
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php
@@ -0,0 +1,114 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\FileFormField;
+
+class FileFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+
+        $this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), '->initialize() sets the value of the field to no file uploaded');
+
+        $node = $this->createNode('textarea', '');
+        try {
+            $field = new FileFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not an input field');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input field');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'text'));
+        try {
+            $field = new FileFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not a file input field');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a file input field');
+        }
+    }
+
+    /**
+     * @dataProvider getSetValueMethods
+     */
+    public function testSetValue($method)
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+
+        $field->$method(null);
+        $this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), "->$method() clears the uploaded file if the value is null");
+
+        $field->$method(__FILE__);
+        $value = $field->getValue();
+
+        $this->assertEquals(basename(__FILE__), $value['name'], "->$method() sets the name of the file field");
+        $this->assertEquals('', $value['type'], "->$method() sets the type of the file field");
+        $this->assertInternalType('string', $value['tmp_name'], "->$method() sets the tmp_name of the file field");
+        $this->assertFileExists($value['tmp_name'], "->$method() creates a copy of the file at the tmp_name path");
+        $this->assertEquals(0, $value['error'], "->$method() sets the error of the file field");
+        $this->assertEquals(filesize(__FILE__), $value['size'], "->$method() sets the size of the file field");
+
+        $origInfo = pathinfo(__FILE__);
+        $tmpInfo = pathinfo($value['tmp_name']);
+        $this->assertEquals(
+            $origInfo['extension'],
+            $tmpInfo['extension'],
+            "->$method() keeps the same file extension in the tmp_name copy"
+        );
+
+        $field->$method(__DIR__.'/../Fixtures/no-extension');
+        $value = $field->getValue();
+
+        $this->assertArrayNotHasKey(
+            'extension',
+            pathinfo($value['tmp_name']),
+            "->$method() does not add a file extension in the tmp_name copy"
+        );
+    }
+
+    public function getSetValueMethods()
+    {
+        return array(
+            array('setValue'),
+            array('upload'),
+        );
+    }
+
+    public function testSetErrorCode()
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+
+        $field->setErrorCode(UPLOAD_ERR_FORM_SIZE);
+        $value = $field->getValue();
+        $this->assertEquals(UPLOAD_ERR_FORM_SIZE, $value['error'], '->setErrorCode() sets the file input field error code');
+
+        try {
+            $field->setErrorCode('foobar');
+            $this->fail('->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
+        }
+    }
+
+    public function testSetRawFilePath()
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+        $field->setFilePath(__FILE__);
+
+        $this->assertEquals(__FILE__, $field->getValue());
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTest.php
new file mode 100644
index 0000000..510f762
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTest.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\InputFormField;
+
+class FormFieldTest extends FormFieldTestCase
+{
+    public function testGetName()
+    {
+        $node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
+        $field = new InputFormField($node);
+
+        $this->assertEquals('name', $field->getName(), '->getName() returns the name of the field');
+    }
+
+    public function testGetSetHasValue()
+    {
+        $node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
+        $field = new InputFormField($node);
+
+        $this->assertEquals('value', $field->getValue(), '->getValue() returns the value of the field');
+
+        $field->setValue('foo');
+        $this->assertEquals('foo', $field->getValue(), '->setValue() sets the value of the field');
+
+        $this->assertTrue($field->hasValue(), '->hasValue() always returns true');
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTestCase.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTestCase.php
new file mode 100644
index 0000000..26b1b0e
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTestCase.php
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+class FormFieldTestCase extends \PHPUnit_Framework_TestCase
+{
+    protected function createNode($tag, $value, $attributes = array())
+    {
+        $document = new \DOMDocument();
+        $node = $document->createElement($tag, $value);
+
+        foreach ($attributes as $name => $value) {
+            $node->setAttribute($name, $value);
+        }
+
+        return $node;
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php
new file mode 100644
index 0000000..193d301
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\InputFormField;
+
+class InputFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
+        $field = new InputFormField($node);
+
+        $this->assertEquals('value', $field->getValue(), '->initialize() sets the value of the field to the value attribute value');
+
+        $node = $this->createNode('textarea', '');
+        try {
+            $field = new InputFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not an input');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox'));
+        try {
+            $field = new InputFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is a checkbox');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is a checkbox');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        try {
+            $field = new InputFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is a file');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is a file');
+        }
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php
new file mode 100644
index 0000000..5d4d003
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\TextareaFormField;
+
+class TextareaFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('textarea', 'foo bar');
+        $field = new TextareaFormField($node);
+
+        $this->assertEquals('foo bar', $field->getValue(), '->initialize() sets the value of the field to the textarea node value');
+
+        $node = $this->createNode('input', '');
+        try {
+            $field = new TextareaFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not a textarea');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a textarea');
+        }
+
+        // Ensure that valid HTML can be used on a textarea.
+        $node = $this->createNode('textarea', 'foo bar <h1>Baz</h1>');
+        $field = new TextareaFormField($node);
+
+        $this->assertEquals('foo bar <h1>Baz</h1>', $field->getValue(), '->initialize() sets the value of the field to the textarea node value');
+
+        // Ensure that we don't do any DOM manipulation/validation by passing in
+        // "invalid" HTML.
+        $node = $this->createNode('textarea', 'foo bar <h1>Baz</h2>');
+        $field = new TextareaFormField($node);
+
+        $this->assertEquals('foo bar <h1>Baz</h2>', $field->getValue(), '->initialize() sets the value of the field to the textarea node value');
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/no-extension b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/no-extension
new file mode 100644
index 0000000..345e6ae
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/no-extension
@@ -0,0 +1 @@
+Test
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/windows-1250.html b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/windows-1250.html
new file mode 100644
index 0000000..c26dc77
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/windows-1250.html
@@ -0,0 +1,8 @@
+<html>
+    <head>
+        <meta http-equiv="content-type" content="text/html;charset=windows-1250">
+    </head>
+    <body>
+        <p>Žèýù</p>
+    </body>
+</html>
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/FormTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/FormTest.php
new file mode 100644
index 0000000..5fd2b18
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/FormTest.php
@@ -0,0 +1,943 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests;
+
+use Symfony\Component\DomCrawler\Form;
+use Symfony\Component\DomCrawler\FormFieldRegistry;
+use Symfony\Component\DomCrawler\Field;
+
+class FormTest extends \PHPUnit_Framework_TestCase
+{
+    public static function setUpBeforeClass()
+    {
+        // Ensure that the private helper class FormFieldRegistry is loaded
+        class_exists('Symfony\\Component\\DomCrawler\\Form');
+    }
+
+    public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <input type="submit" />
+                <form>
+                    <input type="foo" />
+                </form>
+                <button />
+            </html>
+        ');
+
+        $nodes = $dom->getElementsByTagName('input');
+
+        try {
+            $form = new Form($nodes->item(0), 'http://example.com');
+            $this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
+        }
+
+        try {
+            $form = new Form($nodes->item(1), 'http://example.com');
+            $this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image');
+        }
+
+        $nodes = $dom->getElementsByTagName('button');
+
+        try {
+            $form = new Form($nodes->item(0), 'http://example.com');
+            $this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
+        }
+    }
+
+    /**
+     * __construct() should throw \\LogicException if the form attribute is invalid.
+     *
+     * @expectedException \LogicException
+     */
+    public function testConstructorThrowsExceptionIfNoRelatedForm()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <form id="bar">
+                    <input type="submit" form="nonexistent" />
+                </form>
+                <input type="text" form="nonexistent" />
+                <button />
+            </html>
+        ');
+
+        $nodes = $dom->getElementsByTagName('input');
+
+        $form = new Form($nodes->item(0), 'http://example.com');
+        $form = new Form($nodes->item(1), 'http://example.com');
+    }
+
+    public function testConstructorLoadsOnlyFieldsOfTheRightForm()
+    {
+        $dom = $this->createTestMultipleForm();
+
+        $nodes = $dom->getElementsByTagName('form');
+        $buttonElements = $dom->getElementsByTagName('button');
+
+        $form = new Form($nodes->item(0), 'http://example.com');
+        $this->assertCount(3, $form->all());
+
+        $form = new Form($buttonElements->item(1), 'http://example.com');
+        $this->assertCount(5, $form->all());
+    }
+
+    public function testConstructorHandlesFormAttribute()
+    {
+        $dom = $this->createTestHtml5Form();
+
+        $inputElements = $dom->getElementsByTagName('input');
+        $buttonElements = $dom->getElementsByTagName('button');
+
+        // Tests if submit buttons are correctly assigned to forms
+        $form1 = new Form($buttonElements->item(1), 'http://example.com');
+        $this->assertSame($dom->getElementsByTagName('form')->item(0), $form1->getFormNode(), 'HTML5-compliant form attribute handled incorrectly');
+
+        $form1 = new Form($inputElements->item(3), 'http://example.com');
+        $this->assertSame($dom->getElementsByTagName('form')->item(0), $form1->getFormNode(), 'HTML5-compliant form attribute handled incorrectly');
+
+        $form2 = new Form($buttonElements->item(0), 'http://example.com');
+        $this->assertSame($dom->getElementsByTagName('form')->item(1), $form2->getFormNode(), 'HTML5-compliant form attribute handled incorrectly');
+    }
+
+    public function testConstructorHandlesFormValues()
+    {
+        $dom = $this->createTestHtml5Form();
+
+        $inputElements = $dom->getElementsByTagName('input');
+        $buttonElements = $dom->getElementsByTagName('button');
+
+        $form1 = new Form($inputElements->item(3), 'http://example.com');
+        $form2 = new Form($buttonElements->item(0), 'http://example.com');
+
+        // Tests if form values are correctly assigned to forms
+        $values1 = array(
+            'apples' => array('1', '2'),
+            'form_name' => 'form-1',
+            'button_1' => 'Capture fields',
+            'outer_field' => 'success',
+        );
+        $values2 = array(
+            'oranges' => array('1', '2', '3'),
+            'form_name' => 'form_2',
+            'button_2' => '',
+            'app_frontend_form_type_contact_form_type' => array('contactType' => '', 'firstName' => 'John'),
+        );
+
+        $this->assertEquals($values1, $form1->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly');
+        $this->assertEquals($values2, $form2->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly');
+    }
+
+    public function testMultiValuedFields()
+    {
+        $form = $this->createForm('<form>
+            <input type="text" name="foo[4]" value="foo" disabled="disabled" />
+            <input type="text" name="foo" value="foo" disabled="disabled" />
+            <input type="text" name="foo[2]" value="foo" disabled="disabled" />
+            <input type="text" name="foo[]" value="foo" disabled="disabled" />
+            <input type="text" name="bar[foo][]" value="foo" disabled="disabled" />
+            <input type="text" name="bar[foo][foobar]" value="foo" disabled="disabled" />
+            <input type="submit" />
+        </form>
+        ');
+
+        $this->assertEquals(
+            array_keys($form->all()),
+            array('foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]')
+        );
+
+        $this->assertEquals($form->get('foo[2]')->getValue(), 'foo');
+        $this->assertEquals($form->get('foo[3]')->getValue(), 'foo');
+        $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'foo');
+        $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foo');
+
+        $form['foo[2]'] = 'bar';
+        $form['foo[3]'] = 'bar';
+
+        $this->assertEquals($form->get('foo[2]')->getValue(), 'bar');
+        $this->assertEquals($form->get('foo[3]')->getValue(), 'bar');
+
+        $form['bar'] = array('foo' => array('0' => 'bar', 'foobar' => 'foobar'));
+
+        $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'bar');
+        $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foobar');
+    }
+
+    /**
+     * @dataProvider provideInitializeValues
+     */
+    public function testConstructor($message, $form, $values)
+    {
+        $form = $this->createForm('<form>'.$form.'</form>');
+        $this->assertEquals(
+            $values,
+            array_map(function ($field) {
+                    $class = get_class($field);
+
+                    return array(substr($class, strrpos($class, '\\') + 1), $field->getValue());
+                },
+                $form->all()
+            ),
+            '->getDefaultValues() '.$message
+        );
+    }
+
+    public function provideInitializeValues()
+    {
+        return array(
+            array(
+                'does not take into account input fields without a name attribute',
+                '<input type="text" value="foo" />
+                 <input type="submit" />',
+                array(),
+            ),
+            array(
+                'does not take into account input fields with an empty name attribute value',
+                '<input type="text" name="" value="foo" />
+                 <input type="submit" />',
+                array(),
+            ),
+            array(
+                'takes into account disabled input fields',
+                '<input type="text" name="foo" value="foo" disabled="disabled" />
+                 <input type="submit" />',
+                array('foo' => array('InputFormField', 'foo')),
+            ),
+            array(
+                'appends the submitted button value',
+                '<input type="submit" name="bar" value="bar" />',
+                array('bar' => array('InputFormField', 'bar')),
+            ),
+            array(
+                'appends the submitted button value for Button element',
+                '<button type="submit" name="bar" value="bar">Bar</button>',
+                array('bar' => array('InputFormField', 'bar')),
+            ),
+            array(
+                'appends the submitted button value but not other submit buttons',
+                '<input type="submit" name="bar" value="bar" />
+                 <input type="submit" name="foobar" value="foobar" />',
+                 array('foobar' => array('InputFormField', 'foobar')),
+            ),
+            array(
+                'turns an image input into x and y fields',
+                '<input type="image" name="bar" />',
+                array('bar.x' => array('InputFormField', '0'), 'bar.y' => array('InputFormField', '0')),
+            ),
+            array(
+                'returns textareas',
+                '<textarea name="foo">foo</textarea>
+                 <input type="submit" />',
+                 array('foo' => array('TextareaFormField', 'foo')),
+            ),
+            array(
+                'returns inputs',
+                '<input type="text" name="foo" value="foo" />
+                 <input type="submit" />',
+                 array('foo' => array('InputFormField', 'foo')),
+            ),
+            array(
+                'returns checkboxes',
+                '<input type="checkbox" name="foo" value="foo" checked="checked" />
+                 <input type="submit" />',
+                 array('foo' => array('ChoiceFormField', 'foo')),
+            ),
+            array(
+                'returns not-checked checkboxes',
+                '<input type="checkbox" name="foo" value="foo" />
+                 <input type="submit" />',
+                 array('foo' => array('ChoiceFormField', false)),
+            ),
+            array(
+                'returns radio buttons',
+                '<input type="radio" name="foo" value="foo" />
+                 <input type="radio" name="foo" value="bar" checked="bar" />
+                 <input type="submit" />',
+                 array('foo' => array('ChoiceFormField', 'bar')),
+            ),
+            array(
+                'returns file inputs',
+                '<input type="file" name="foo" />
+                 <input type="submit" />',
+                 array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))),
+            ),
+        );
+    }
+
+    public function testGetFormNode()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><form><input type="submit" /></form></html>');
+
+        $form = new Form($dom->getElementsByTagName('input')->item(0), 'http://example.com');
+
+        $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form');
+    }
+
+    public function testGetFormNodeFromNamedForm()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><form name="my_form"><input type="submit" /></form></html>');
+
+        $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com');
+
+        $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form');
+    }
+
+    public function testGetMethod()
+    {
+        $form = $this->createForm('<form><input type="submit" /></form>');
+        $this->assertEquals('GET', $form->getMethod(), '->getMethod() returns get if no method is defined');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>');
+        $this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>', 'put');
+        $this->assertEquals('PUT', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>', 'delete');
+        $this->assertEquals('DELETE', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>', 'patch');
+        $this->assertEquals('PATCH', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
+    }
+
+    public function testGetSetValue()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>');
+
+        $this->assertEquals('foo', $form['foo']->getValue(), '->offsetGet() returns the value of a form field');
+
+        $form['foo'] = 'bar';
+
+        $this->assertEquals('bar', $form['foo']->getValue(), '->offsetSet() changes the value of a form field');
+
+        try {
+            $form['foobar'] = 'bar';
+            $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        }
+
+        try {
+            $form['foobar'];
+            $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        }
+    }
+
+    public function testSetValueOnMultiValuedFieldsWithMalformedName()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo[bar]" value="bar" /><input type="text" name="foo[baz]" value="baz" /><input type="submit" /></form>');
+
+        try {
+            $form['foo[bar'] = 'bar';
+            $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.');
+        }
+    }
+
+    public function testDisableValidation()
+    {
+        $form = $this->createForm('<form>
+            <select name="foo[bar]">
+                <option value="bar">bar</option>
+            </select>
+            <select name="foo[baz]">
+                <option value="foo">foo</option>
+            </select>
+            <input type="submit" />
+        </form>');
+
+        $form->disableValidation();
+
+        $form['foo[bar]']->select('foo');
+        $form['foo[baz]']->select('bar');
+        $this->assertEquals('foo', $form['foo[bar]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.');
+        $this->assertEquals('bar', $form['foo[baz]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.');
+    }
+
+    public function testOffsetUnset()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>');
+        unset($form['foo']);
+        $this->assertFalse(isset($form['foo']), '->offsetUnset() removes a field');
+    }
+
+    public function testOffsetExists()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>');
+
+        $this->assertTrue(isset($form['foo']), '->offsetExists() return true if the field exists');
+        $this->assertFalse(isset($form['bar']), '->offsetExists() return false if the field does not exist');
+    }
+
+    public function testGetValues()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><select multiple="multiple" name="baz[]"></select><input type="submit" /></form>');
+        $this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => array()), $form->getValues(), '->getValues() returns all form field values');
+
+        $form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes');
+
+        $form = $this->createForm('<form><input type="file" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields');
+
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" disabled="disabled" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields');
+    }
+
+    public function testSetValues()
+    {
+        $form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" checked="checked" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $form->setValues(array('foo' => false, 'bar' => 'foo'));
+        $this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields');
+    }
+
+    public function testMultiselectSetValues()
+    {
+        $form = $this->createForm('<form><select multiple="multiple" name="multi"><option value="foo">foo</option><option value="bar">bar</option></select><input type="submit" /></form>');
+        $form->setValues(array('multi' => array("foo", "bar")));
+        $this->assertEquals(array('multi' => array('foo', 'bar')), $form->getValues(), '->setValue() sets the values of select');
+    }
+
+    public function testGetPhpValues()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays');
+
+        $form = $this->createForm('<form><input type="text" name="fo.o[ba.r]" value="foo" /><input type="text" name="ba r" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('fo.o' => array('ba.r' => 'foo'), 'ba r' => 'bar'), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names');
+
+        $form = $this->createForm('<form><input type="text" name="fo.o[ba.r][]" value="foo" /><input type="text" name="fo.o[ba.r][ba.z]" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('fo.o' => array('ba.r' => array('foo', 'ba.z' => 'bar'))), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively');
+
+        $form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><select multiple="multiple" name="baz[]"></select><input type="submit" /></form>');
+        $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), "->getPhpValues() doesn't return empty values");
+    }
+
+    public function testGetFiles()
+    {
+        $form = $this->createForm('<form><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for POST');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'put');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PUT');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'delete');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for DELETE');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'patch');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PATCH');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" disabled="disabled" /><input type="submit" /></form>');
+        $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields');
+    }
+
+    public function testGetPhpFiles()
+    {
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="f.o o[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('f.o o' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="f.o o[bar][ba.z]" /><input type="file" name="f.o o[bar][]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('f.o o' => array('bar' => array('ba.z' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0), array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively');
+    }
+
+    /**
+     * @dataProvider provideGetUriValues
+     */
+    public function testGetUri($message, $form, $values, $uri, $method = null)
+    {
+        $form = $this->createForm($form, $method);
+        $form->setValues($values);
+
+        $this->assertEquals('http://example.com'.$uri, $form->getUri(), '->getUri() '.$message);
+    }
+
+    public function testGetBaseUri()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<form method="post" action="foo.php"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $nodes = $dom->getElementsByTagName('input');
+        $form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/');
+        $this->assertEquals('http://www.foo.com/foo.php', $form->getUri());
+    }
+
+    public function testGetUriWithAnchor()
+    {
+        $form = $this->createForm('<form action="#foo"><input type="submit" /></form>', null, 'http://example.com/id/123');
+
+        $this->assertEquals('http://example.com/id/123#foo', $form->getUri());
+    }
+
+    public function testGetUriActionAbsolute()
+    {
+        $formHtml = '<form id="login_form" action="https://login.foo.com/login.php?login_attempt=1" method="POST"><input type="text" name="foo" value="foo" /><input type="submit" /></form>';
+
+        $form = $this->createForm($formHtml);
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        $form = $this->createForm($formHtml, null, 'https://login.foo.com');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        $form = $this->createForm($formHtml, null, 'https://login.foo.com/bar/');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        // The action URI haven't the same domain Host have an another domain as Host
+        $form = $this->createForm($formHtml, null, 'https://www.foo.com');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        $form = $this->createForm($formHtml, null, 'https://www.foo.com/bar/');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+    }
+
+    public function testGetUriAbsolute()
+    {
+        $form = $this->createForm('<form action="foo"><input type="submit" /></form>', null, 'http://localhost/foo/');
+        $this->assertEquals('http://localhost/foo/foo', $form->getUri(), '->getUri() returns absolute URIs');
+
+        $form = $this->createForm('<form action="/foo"><input type="submit" /></form>', null, 'http://localhost/foo/');
+        $this->assertEquals('http://localhost/foo', $form->getUri(), '->getUri() returns absolute URIs');
+    }
+
+    public function testGetUriWithOnlyQueryString()
+    {
+        $form = $this->createForm('<form action="?get=param"><input type="submit" /></form>', null, 'http://localhost/foo/bar');
+        $this->assertEquals('http://localhost/foo/bar?get=param', $form->getUri(), '->getUri() returns absolute URIs only if the host has been defined in the constructor');
+    }
+
+    public function testGetUriWithoutAction()
+    {
+        $form = $this->createForm('<form><input type="submit" /></form>', null, 'http://localhost/foo/bar');
+        $this->assertEquals('http://localhost/foo/bar', $form->getUri(), '->getUri() returns path if no action defined');
+    }
+
+    public function provideGetUriValues()
+    {
+        return array(
+            array(
+                'returns the URI of the form',
+                '<form action="/foo"><input type="submit" /></form>',
+                array(),
+                '/foo',
+            ),
+            array(
+                'appends the form values if the method is get',
+                '<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo?foo=foo',
+            ),
+            array(
+                'appends the form values and merges the submitted values',
+                '<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array('foo' => 'bar'),
+                '/foo?foo=bar',
+            ),
+            array(
+                'does not append values if the method is post',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo',
+            ),
+            array(
+                'does not append values if the method is patch',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo',
+                'PUT',
+            ),
+            array(
+                'does not append values if the method is delete',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo',
+                'DELETE',
+            ),
+            array(
+                'does not append values if the method is put',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo',
+                'PATCH',
+            ),
+            array(
+                'appends the form values to an existing query string',
+                '<form action="/foo?bar=bar"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo?bar=bar&foo=foo',
+            ),
+            array(
+                'returns an empty URI if the action is empty',
+                '<form><input type="submit" /></form>',
+                array(),
+                '/',
+            ),
+            array(
+                'appends the form values even if the action is empty',
+                '<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/?foo=foo',
+            ),
+            array(
+                'chooses the path if the action attribute value is a sharp (#)',
+                '<form action="#" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/#',
+            ),
+        );
+    }
+
+    public function testHas()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $this->assertFalse($form->has('foo'), '->has() returns false if a field is not in the form');
+        $this->assertTrue($form->has('bar'), '->has() returns true if a field is in the form');
+    }
+
+    public function testRemove()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $form->remove('bar');
+        $this->assertFalse($form->has('bar'), '->remove() removes a field');
+    }
+
+    public function testGet()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $form->get('bar'), '->get() returns the field object associated with the given name');
+
+        try {
+            $form->get('foo');
+            $this->fail('->get() throws an \InvalidArgumentException if the field does not exist');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->get() throws an \InvalidArgumentException if the field does not exist');
+        }
+    }
+
+    public function testAll()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $fields = $form->all();
+        $this->assertCount(1, $fields, '->all() return an array of form field objects');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $fields['bar'], '->all() return an array of form field objects');
+    }
+
+    public function testSubmitWithoutAFormButton()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <form>
+                    <input type="foo" />
+                </form>
+            </html>
+        ');
+
+        $nodes = $dom->getElementsByTagName('form');
+        $form = new Form($nodes->item(0), 'http://example.com');
+        $this->assertSame($nodes->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form');
+    }
+
+    public function testTypeAttributeIsCaseInsensitive()
+    {
+        $form = $this->createForm('<form method="post"><input type="IMAGE" name="example" /></form>');
+        $this->assertTrue($form->has('example.x'), '->has() returns true if the image input was correctly turned into an x and a y fields');
+        $this->assertTrue($form->has('example.y'), '->has() returns true if the image input was correctly turned into an x and a y fields');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryAddThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('[foo]'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryRemoveThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->remove('[foo]');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryGetThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->get('[foo]');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->get('foo');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistrySetThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->set('[foo]', null);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->set('foo', null);
+    }
+
+    public function testFormFieldRegistryHasReturnsTrueWhenTheFQNExists()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('foo[bar]'));
+
+        $this->assertTrue($registry->has('foo'));
+        $this->assertTrue($registry->has('foo[bar]'));
+        $this->assertFalse($registry->has('bar'));
+        $this->assertFalse($registry->has('foo[foo]'));
+    }
+
+    public function testFormRegistryFieldsCanBeRemoved()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('foo'));
+        $registry->remove('foo');
+        $this->assertFalse($registry->has('foo'));
+    }
+
+    public function testFormRegistrySupportsMultivaluedFields()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('foo[]'));
+        $registry->add($this->getFormFieldMock('foo[]'));
+        $registry->add($this->getFormFieldMock('bar[5]'));
+        $registry->add($this->getFormFieldMock('bar[]'));
+        $registry->add($this->getFormFieldMock('bar[baz]'));
+
+        $this->assertEquals(
+            array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'),
+            array_keys($registry->all())
+        );
+    }
+
+    public function testFormRegistrySetValues()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($f2 = $this->getFormFieldMock('foo[2]'));
+        $registry->add($f3 = $this->getFormFieldMock('foo[3]'));
+        $registry->add($fbb = $this->getFormFieldMock('foo[bar][baz]'));
+
+        $f2
+            ->expects($this->exactly(2))
+            ->method('setValue')
+            ->with(2)
+        ;
+
+        $f3
+            ->expects($this->exactly(2))
+            ->method('setValue')
+            ->with(3)
+        ;
+
+        $fbb
+            ->expects($this->exactly(2))
+            ->method('setValue')
+            ->with('fbb')
+        ;
+
+        $registry->set('foo[2]', 2);
+        $registry->set('foo[3]', 3);
+        $registry->set('foo[bar][baz]', 'fbb');
+
+        $registry->set('foo', array(
+            2 => 2,
+            3 => 3,
+            'bar' => array(
+                'baz' => 'fbb',
+             ),
+        ));
+    }
+
+    public function testDifferentFieldTypesWithSameName()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <body>
+                    <form action="/">
+                        <input type="hidden" name="option" value="default">
+                        <input type="radio" name="option" value="A">
+                        <input type="radio" name="option" value="B">
+                        <input type="hidden" name="settings[1]" value="0">
+                        <input type="checkbox" name="settings[1]" value="1" id="setting-1">
+                        <button>klickme</button>
+                    </form>
+                </body>
+            </html>
+        ');
+        $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com');
+
+        $this->assertInstanceOf('Symfony\Component\DomCrawler\Field\ChoiceFormField', $form->get('option'));
+    }
+
+    protected function getFormFieldMock($name, $value = null)
+    {
+        $field = $this
+            ->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField')
+            ->setMethods(array('getName', 'getValue', 'setValue', 'initialize'))
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $field
+            ->expects($this->any())
+            ->method('getName')
+            ->will($this->returnValue($name))
+        ;
+
+        $field
+            ->expects($this->any())
+            ->method('getValue')
+            ->will($this->returnValue($value))
+        ;
+
+        return $field;
+    }
+
+    protected function createForm($form, $method = null, $currentUri = null)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html>'.$form.'</html>');
+
+        $xPath = new \DOMXPath($dom);
+        $nodes = $xPath->query('//input | //button');
+
+        if (null === $currentUri) {
+            $currentUri = 'http://example.com/';
+        }
+
+        return new Form($nodes->item($nodes->length - 1), $currentUri, $method);
+    }
+
+    protected function createTestHtml5Form()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+        <html>
+            <h1>Hello form</h1>
+            <form id="form-1" action="" method="POST">
+                <div><input type="checkbox" name="apples[]" value="1" checked /></div>
+                <input form="form_2" type="checkbox" name="oranges[]" value="1" checked />
+                <div><label></label><input form="form-1" type="hidden" name="form_name" value="form-1" /></div>
+                <input form="form-1" type="submit" name="button_1" value="Capture fields" />
+                <button form="form_2" type="submit" name="button_2">Submit form_2</button>
+            </form>
+            <input form="form-1" type="checkbox" name="apples[]" value="2" checked />
+            <form id="form_2" action="" method="POST">
+                <div><div><input type="checkbox" name="oranges[]" value="2" checked />
+                <input type="checkbox" name="oranges[]" value="3" checked /></div></div>
+                <input form="form_2" type="hidden" name="form_name" value="form_2" />
+                <input form="form-1" type="hidden" name="outer_field" value="success" />
+                <button form="form-1" type="submit" name="button_3">Submit from outside the form</button>
+                <div>
+                    <label for="app_frontend_form_type_contact_form_type_contactType">Message subject</label>
+                    <div>
+                        <select name="app_frontend_form_type_contact_form_type[contactType]" id="app_frontend_form_type_contact_form_type_contactType"><option selected="selected" value="">Please select subject</option><option id="1">Test type</option></select>
+                    </div>
+                </div>
+                <div>
+                    <label for="app_frontend_form_type_contact_form_type_firstName">Firstname</label>
+                    <input type="text" name="app_frontend_form_type_contact_form_type[firstName]" value="John" id="app_frontend_form_type_contact_form_type_firstName"/>
+                </div>
+            </form>
+            <button />
+        </html>');
+
+        return $dom;
+    }
+
+    protected function createTestMultipleForm()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+        <html>
+            <h1>Hello form</h1>
+            <form action="" method="POST">
+                <div><input type="checkbox" name="apples[]" value="1" checked /></div>
+                <input type="checkbox" name="oranges[]" value="1" checked />
+                <div><label></label><input type="hidden" name="form_name" value="form-1" /></div>
+                <input type="submit" name="button_1" value="Capture fields" />
+                <button type="submit" name="button_2">Submit form_2</button>
+            </form>
+            <form action="" method="POST">
+                <div><div><input type="checkbox" name="oranges[]" value="2" checked />
+                <input type="checkbox" name="oranges[]" value="3" checked /></div></div>
+                <input type="hidden" name="form_name" value="form_2" />
+                <input type="hidden" name="outer_field" value="success" />
+                <button type="submit" name="button_3">Submit from outside the form</button>
+            </form>
+            <button />
+        </html>');
+
+        return $dom;
+    }
+
+    public function testgetPhpValuesWithEmptyTextarea()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+              <html>
+                  <form>
+                      <textarea name="example"></textarea>
+                  </form>
+              </html>
+          ');
+
+        $nodes = $dom->getElementsByTagName('form');
+        $form = new Form($nodes->item(0), 'http://example.com');
+        $this->assertEquals($form->getPhpValues(), array('example' => ''));
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/LinkTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/LinkTest.php
new file mode 100644
index 0000000..98a45a3
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/LinkTest.php
@@ -0,0 +1,160 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests;
+
+use Symfony\Component\DomCrawler\Link;
+
+class LinkTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException \LogicException
+     */
+    public function testConstructorWithANonATag()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><div><div></html>');
+
+        new Link($dom->getElementsByTagName('div')->item(0), 'http://www.example.com/');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testConstructorWithAnInvalidCurrentUri()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><a href="/foo">foo</a></html>');
+
+        new Link($dom->getElementsByTagName('a')->item(0), 'example.com');
+    }
+
+    public function testGetNode()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><a href="/foo">foo</a></html>');
+
+        $node = $dom->getElementsByTagName('a')->item(0);
+        $link = new Link($node, 'http://example.com/');
+
+        $this->assertEquals($node, $link->getNode(), '->getNode() returns the node associated with the link');
+    }
+
+    public function testGetMethod()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><a href="/foo">foo</a></html>');
+
+        $node = $dom->getElementsByTagName('a')->item(0);
+        $link = new Link($node, 'http://example.com/');
+
+        $this->assertEquals('GET', $link->getMethod(), '->getMethod() returns the method of the link');
+
+        $link = new Link($node, 'http://example.com/', 'post');
+        $this->assertEquals('POST', $link->getMethod(), '->getMethod() returns the method of the link');
+    }
+
+    /**
+     * @dataProvider getGetUriTests
+     */
+    public function testGetUri($url, $currentUri, $expected)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML(sprintf('<html><a href="%s">foo</a></html>', $url));
+        $link = new Link($dom->getElementsByTagName('a')->item(0), $currentUri);
+
+        $this->assertEquals($expected, $link->getUri());
+    }
+
+    /**
+     * @dataProvider getGetUriTests
+     */
+    public function testGetUriOnArea($url, $currentUri, $expected)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML(sprintf('<html><map><area href="%s" /></map></html>', $url));
+        $link = new Link($dom->getElementsByTagName('area')->item(0), $currentUri);
+
+        $this->assertEquals($expected, $link->getUri());
+    }
+
+    /**
+     * @dataProvider getGetUriTests
+     */
+    public function testGetUriOnLink($url, $currentUri, $expected)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML(sprintf('<html><head><link href="%s" /></head></html>', $url));
+        $link = new Link($dom->getElementsByTagName('link')->item(0), $currentUri);
+
+        $this->assertEquals($expected, $link->getUri());
+    }
+
+    public function getGetUriTests()
+    {
+        return array(
+            array('/foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
+            array('/foo', 'http://localhost/bar/foo', 'http://localhost/foo'),
+            array('
+            /foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
+            array('/foo
+            ', 'http://localhost/bar/foo', 'http://localhost/foo'),
+
+            array('foo', 'http://localhost/bar/foo/', 'http://localhost/bar/foo/foo'),
+            array('foo', 'http://localhost/bar/foo', 'http://localhost/bar/foo'),
+
+            array('', 'http://localhost/bar/', 'http://localhost/bar/'),
+            array('#', 'http://localhost/bar/', 'http://localhost/bar/#'),
+            array('#bar', 'http://localhost/bar?a=b', 'http://localhost/bar?a=b#bar'),
+            array('#bar', 'http://localhost/bar/#foo', 'http://localhost/bar/#bar'),
+            array('?a=b', 'http://localhost/bar#foo', 'http://localhost/bar?a=b'),
+            array('?a=b', 'http://localhost/bar/', 'http://localhost/bar/?a=b'),
+
+            array('http://login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'),
+            array('https://login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'),
+            array('mailto:foo@bar.com', 'http://localhost/foo', 'mailto:foo@bar.com'),
+
+            // tests schema relative URL (issue #7169)
+            array('//login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'),
+            array('//login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'),
+
+            array('?foo=2', 'http://localhost?foo=1', 'http://localhost?foo=2'),
+            array('?foo=2', 'http://localhost/?foo=1', 'http://localhost/?foo=2'),
+            array('?foo=2', 'http://localhost/bar?foo=1', 'http://localhost/bar?foo=2'),
+            array('?foo=2', 'http://localhost/bar/?foo=1', 'http://localhost/bar/?foo=2'),
+            array('?bar=2', 'http://localhost?foo=1', 'http://localhost?bar=2'),
+
+            array('foo', 'http://login.foo.com/bar/baz?/query/string', 'http://login.foo.com/bar/foo'),
+
+            array('.', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'),
+            array('./', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'),
+            array('./foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/foo'),
+            array('..', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'),
+            array('../', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'),
+            array('../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/foo'),
+            array('../..', 'http://localhost/foo/bar/baz', 'http://localhost/'),
+            array('../../', 'http://localhost/foo/bar/baz', 'http://localhost/'),
+            array('../../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo'),
+            array('../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
+            array('../bar/../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
+            array('../bar/./../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
+            array('../../', 'http://localhost/', 'http://localhost/'),
+            array('../../', 'http://localhost', 'http://localhost/'),
+
+            array('/foo', 'http://localhost?bar=1', 'http://localhost/foo'),
+            array('/foo', 'http://localhost#bar', 'http://localhost/foo'),
+            array('/foo', 'file:///', 'file:///foo'),
+            array('/foo', 'file:///bar/baz', 'file:///foo'),
+            array('foo', 'file:///', 'file:///foo'),
+            array('foo', 'file:///bar/baz', 'file:///bar/foo'),
+        );
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/composer.json b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/composer.json
new file mode 100644
index 0000000..5bc3192
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/composer.json
@@ -0,0 +1,37 @@
+{
+    "name": "symfony/dom-crawler",
+    "type": "library",
+    "description": "Symfony DomCrawler Component",
+    "keywords": [],
+    "homepage": "http://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3"
+    },
+    "require-dev": {
+        "symfony/css-selector": "~2.3"
+    },
+    "suggest": {
+        "symfony/css-selector": ""
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\DomCrawler\\": "" }
+    },
+    "target-dir": "Symfony/Component/DomCrawler",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.6-dev"
+        }
+    }
+}
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/phpunit.xml.dist b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/phpunit.xml.dist
new file mode 100644
index 0000000..5ce15ec
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/phpunit.xml.dist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+>
+    <php>
+        <!-- Silence E_USER_DEPRECATED (-16385 == -1 & ~E_USER_DEPRECATED) -->
+        <ini name="error_reporting" value="-16385"/>
+    </php>
+
+    <testsuites>
+        <testsuite name="Symfony DomCrawler Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
