diff --git a/composer.json b/composer.json
index 722f1fa..4209f8a 100644
--- a/composer.json
+++ b/composer.json
@@ -17,6 +17,7 @@
     "symfony/validator": "2.6.*",
     "symfony/process": "2.6.*",
     "symfony/yaml": "2.6.*",
+    "symfony/dom-crawler": "2.6.*",
     "twig/twig": "1.16.*",
     "doctrine/common": "dev-master#a45d110f71c323e29f41eb0696fa230e3fa1b1b5",
     "doctrine/annotations": "1.2.*",
@@ -28,7 +29,8 @@
     "zendframework/zend-feed": "2.3.*",
     "mikey179/vfsStream": "1.*",
     "stack/builder": "1.0.*",
-    "egulias/email-validator": "1.2.*"
+    "egulias/email-validator": "1.2.*",
+    "phpunit/phpunit-dom-assertions": "dev-master#4e43c66e35fb5e30a0eeb535c861455540c0d9fb"
   },
   "autoload": {
     "psr-4": {
diff --git a/composer.lock b/composer.lock
index fe5dc0a..3a17792 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "461047a83f0f5554064bfe97e2a673fb",
+    "hash": "4d34ebabb8ae84edecae52b750f21094",
     "packages": [
         {
             "name": "doctrine/annotations",
@@ -1088,6 +1088,63 @@
             "time": "2014-07-18 07:15:58"
         },
         {
+            "name": "phpunit/phpunit-dom-assertions",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpunit/phpunit-dom-assertions.git",
+                "reference": "4e43c66e35fb5e30a0eeb535c861455540c0d9fb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpunit/phpunit-dom-assertions/zipball/4e43c66e35fb5e30a0eeb535c861455540c0d9fb",
+                "reference": "4e43c66e35fb5e30a0eeb535c861455540c0d9fb",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "phpunit/phpunit": "~4.0",
+                "symfony/css-selector": "~2.0",
+                "symfony/dom-crawler": "~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "DOM assertions for PHPUnit",
+            "homepage": "http://github.com/phpunit/phpunit-dom-assertions",
+            "keywords": [
+                "Xpath",
+                "assertions",
+                "css",
+                "dom",
+                "phpunit",
+                "tests"
+            ],
+            "time": "2014-08-27 18:14:08"
+        },
+        {
             "name": "phpunit/phpunit-mock-objects",
             "version": "dev-master",
             "source": {
@@ -1860,6 +1917,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",
@@ -2594,7 +2704,8 @@
     "minimum-stability": "stable",
     "stability-flags": {
         "doctrine/common": 20,
-        "phpunit/phpunit-mock-objects": 20
+        "phpunit/phpunit-mock-objects": 20,
+        "phpunit/phpunit-dom-assertions": 20
     },
     "prefer-stable": false,
     "prefer-lowest": false,
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 1a0ac77..b52f42a 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -16,7 +16,7 @@
  *
  * @ingroup testing
  */
-abstract class UnitTestCase extends \PHPUnit_Framework_TestCase {
+abstract class UnitTestCase extends \PHPUnit_Framework_DOMTestCase {
 
   /**
    * The random generator.
diff --git a/core/vendor/composer/autoload_classmap.php b/core/vendor/composer/autoload_classmap.php
index 670145c..1c53039 100644
--- a/core/vendor/composer/autoload_classmap.php
+++ b/core/vendor/composer/autoload_classmap.php
@@ -59,6 +59,7 @@
     'PHPUnit_Framework_Constraint_TraversableContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php',
     'PHPUnit_Framework_Constraint_TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php',
     'PHPUnit_Framework_Constraint_Xor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Xor.php',
+    'PHPUnit_Framework_DOMTestCase' => $vendorDir . '/phpunit/phpunit-dom-assertions/src/DOMTestCase.php',
     'PHPUnit_Framework_Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Error.php',
     'PHPUnit_Framework_Error_Deprecated' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Deprecated.php',
     'PHPUnit_Framework_Error_Notice' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Notice.php',
diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php
index 54e2998..3093c9b 100644
--- a/core/vendor/composer/autoload_namespaces.php
+++ b/core/vendor/composer/autoload_namespaces.php
@@ -20,6 +20,7 @@
     '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'),
diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json
index 6104e35..564eeb8 100644
--- a/core/vendor/composer/installed.json
+++ b/core/vendor/composer/installed.json
@@ -2674,5 +2674,119 @@
             "feed",
             "zf2"
         ]
+    },
+    {
+        "name": "phpunit/phpunit-dom-assertions",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/phpunit/phpunit-dom-assertions.git",
+            "reference": "4e43c66e35fb5e30a0eeb535c861455540c0d9fb"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/phpunit/phpunit-dom-assertions/zipball/4e43c66e35fb5e30a0eeb535c861455540c0d9fb",
+            "reference": "4e43c66e35fb5e30a0eeb535c861455540c0d9fb",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3",
+            "phpunit/phpunit": "~4.0",
+            "symfony/css-selector": "~2.0",
+            "symfony/dom-crawler": "~2.0"
+        },
+        "time": "2014-08-27 18:14:08",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "classmap": [
+                "src/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Jeff Welch",
+                "email": "whatthejeff@gmail.com"
+            },
+            {
+                "name": "Sebastian Bergmann",
+                "email": "sebastian@phpunit.de"
+            }
+        ],
+        "description": "DOM assertions for PHPUnit",
+        "homepage": "http://github.com/phpunit/phpunit-dom-assertions",
+        "keywords": [
+            "Xpath",
+            "assertions",
+            "css",
+            "dom",
+            "phpunit",
+            "tests"
+        ]
+    },
+    {
+        "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"
     }
 ]
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/.gitignore b/core/vendor/phpunit/phpunit-dom-assertions/.gitignore
new file mode 100644
index 0000000..9fa3217
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/.gitignore
@@ -0,0 +1,4 @@
+/composer.lock
+/composer.phar
+/phpunit.xml
+/vendor
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/.travis.yml b/core/vendor/phpunit/phpunit-dom-assertions/.travis.yml
new file mode 100644
index 0000000..a354613
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/.travis.yml
@@ -0,0 +1,17 @@
+language: php
+
+php:
+    - 5.3.3
+    - 5.3
+    - 5.4
+    - 5.5
+    - 5.6
+    - hhvm
+
+before_script:
+    - composer install --prefer-source
+
+script: vendor/bin/phpunit
+
+notifications:
+    email: false
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/LICENSE b/core/vendor/phpunit/phpunit-dom-assertions/LICENSE
new file mode 100644
index 0000000..6ba5790
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/LICENSE
@@ -0,0 +1,33 @@
+PHPUnit
+
+Copyright (c) 2014, Sebastian Bergmann <sebastian@phpunit.de>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+ * Neither the name of Sebastian Bergmann nor the names of his
+   contributors may be used to endorse or promote products derived
+   from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/README.md b/core/vendor/phpunit/phpunit-dom-assertions/README.md
new file mode 100644
index 0000000..cf11be9
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/README.md
@@ -0,0 +1,66 @@
+# PHPUnit DOM Assertions
+
+A work in progress, drop-in replacement for the following deprecated PHPUnit assertions:
+
+ * `assertSelectCount()`
+ * `assertSelectRegExp()`
+ * `assertSelectEquals()`
+ * `assertTag()` (not yet ported)
+ * `assertNotTag()` (not yet ported)
+
+# TODO
+
+ * Port `assertTag()` and `assertNotTag()`.
+ * Improve tests.
+ * Improve error messages.
+ * Improve comments and documentation.
+ * Add XPath support.
+
+## Requirements
+
+The PHPUnit DOM assertions require PHP 5.3.3 or later.
+
+## Installation
+
+The recommended way to install the PHPUnit DOM assertions is
+[through composer](http://getcomposer.org). Just create a `composer.json` file
+and run the `php composer.phar install` command to install it:
+
+~~~json
+{
+    "require-dev": {
+        "phpunit/phpunit-dom-assertions": "1.0.*@dev"
+    }
+}
+~~~
+
+## Usage
+
+Extend `PHPUnit_Framework_DOMTestCase` to use the DOM assertions:
+
+~~~php
+class DOMTest extends PHPUnit_Framework_DOMTestCase
+{
+    public function testSelectEquals()
+    {
+        $html = file_get_contents('test.html');
+        $selector = 'span.test_class';
+        $content  = 'Test Class Text';
+
+        $this->assertSelectEquals($selector, $content, true, $html);
+    }
+}
+~~~
+
+## Tests
+
+[![Build Status](https://travis-ci.org/phpunit/phpunit-dom-assertions.png?branch=master)](https://travis-ci.org/phpunit/phpunit-dom-assertions)
+
+To run the test suite, you need [composer](http://getcomposer.org).
+
+    $ php composer.phar install
+    $ vendor/bin/phpunit
+
+## License
+
+The PHPUnit DOM assertions library is licensed under the [BSD 3-Clause license](LICENSE).
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/composer.json b/core/vendor/phpunit/phpunit-dom-assertions/composer.json
new file mode 100644
index 0000000..2b963ee
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "phpunit/phpunit-dom-assertions",
+    "description": "DOM assertions for PHPUnit",
+    "keywords": ["dom","xpath","css","assertions","phpunit","tests"],
+    "homepage": "http://github.com/phpunit/phpunit-dom-assertions",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Sebastian Bergmann",
+            "email": "sebastian@phpunit.de"
+        },
+        {
+            "name": "Jeff Welch",
+            "email": "whatthejeff@gmail.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3",
+        "symfony/css-selector": "~2.0",
+        "symfony/dom-crawler": "~2.0",
+        "phpunit/phpunit": "~4.0"
+    },
+    "autoload": {
+        "classmap": [
+            "src/"
+        ]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/phpunit.xml.dist b/core/vendor/phpunit/phpunit-dom-assertions/phpunit.xml.dist
new file mode 100644
index 0000000..8cb18c0
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/phpunit.xml.dist
@@ -0,0 +1,24 @@
+<?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="PHPUnit DOM Assertions Test Suite">
+            <directory suffix="Test.php">./tests</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist>
+            <directory>./src</directory>
+        </whitelist>
+    </filter>
+</phpunit>
\ No newline at end of file
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/src/DOMTestCase.php b/core/vendor/phpunit/phpunit-dom-assertions/src/DOMTestCase.php
new file mode 100644
index 0000000..6a53629
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/src/DOMTestCase.php
@@ -0,0 +1,192 @@
+<?php
+/**
+ * PHPUnit
+ *
+ * Copyright (c) 2001-2014, Sebastian Bergmann <sebastian@phpunit.de>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @package    PHPUnit_Framework_DOMTestCase
+ * @author     Sebastian Bergmann <sebastian@phpunit.de>
+ * @author     Jeff Welch <whatthejeff@gmail.com>
+ * @copyright  2014 Sebastian Bergmann <sebastian@phpunit.de>
+ * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
+ * @link       http://github.com/phpunit/phpunit-dom-assertions
+ * @since      File available since Release 1.0.0
+ */
+
+use Symfony\Component\DomCrawler\Crawler;
+
+/**
+ *
+ *
+ * @package    PHPUnit_Framework_DOMTestCase
+ * @author     Sebastian Bergmann <sebastian@phpunit.de>
+ * @author     Jeff Welch <whatthejeff@gmail.com>
+ * @copyright  2014 Sebastian Bergmann <sebastian@phpunit.de>
+ * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
+ * @link       http://github.com/phpunit/phpunit-dom-assertions
+ * @since      Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Framework_DOMTestCase extends PHPUnit_Framework_TestCase
+{
+    /**
+     * Assert the presence, absence, or count of elements in a document matching
+     * the CSS $selector, regardless of the contents of those elements.
+     *
+     * The first argument, $selector, is the CSS selector used to match
+     * the elements in the $actual document.
+     *
+     * The second argument, $count, can be either boolean or numeric.
+     * When boolean, it asserts for presence of elements matching the selector
+     * (true) or absence of elements (false).
+     * When numeric, it asserts the count of elements.
+     *
+     * assertSelectCount("#binder", true, $xml);  // any?
+     * assertSelectCount(".binder", 3, $xml);     // exactly 3?
+     *
+     * @param array                 $selector
+     * @param integer|boolean|array $count
+     * @param mixed                 $actual
+     * @param string                $message
+     * @param boolean               $isHtml
+     * @since Method available since Release 1.0.0
+     */
+    public static function assertSelectCount($selector, $count, $actual, $message = '', $isHtml = true)
+    {
+        self::assertSelectEquals(
+            $selector, true, $count, $actual, $message, $isHtml
+        );
+    }
+
+    /**
+     * assertSelectRegExp("#binder .name", "/Mike|Derek/", true, $xml); // any?
+     * assertSelectRegExp("#binder .name", "/Mike|Derek/", 3, $xml);    // 3?
+     *
+     * @param array                 $selector
+     * @param string                $pattern
+     * @param integer|boolean|array $count
+     * @param mixed                 $actual
+     * @param string                $message
+     * @param boolean               $isHtml
+     * @since Method available since Release 1.0.0
+     */
+    public static function assertSelectRegExp($selector, $pattern, $count, $actual, $message = '', $isHtml = true)
+    {
+        self::assertSelectEquals(
+            $selector, "regexp:$pattern", $count, $actual, $message, $isHtml
+        );
+    }
+
+    /**
+     * assertSelectEquals("#binder .name", "Chuck", true,  $xml);  // any?
+     * assertSelectEquals("#binder .name", "Chuck", false, $xml);  // none?
+     *
+     * @param array                 $selector
+     * @param string                $content
+     * @param integer|boolean|array $count
+     * @param mixed                 $actual
+     * @param string                $message
+     * @param boolean               $isHtml
+     * @since Method available since Release 1.0.0
+     *
+     * @throws PHPUnit_Framework_Exception
+     */
+    public static function assertSelectEquals($selector, $content, $count, $actual, $message = '', $isHtml = true)
+    {
+        $crawler = new Crawler;
+
+        if ($actual instanceof DOMDocument) {
+            $crawler->addDocument($actual);
+        } else if ($isHtml) {
+            $crawler->addHtmlContent($actual);
+        } else {
+            $crawler->addXmlContent($actual);
+        }
+
+        $crawler = $crawler->filter($selector);
+
+        if (is_string($content)) {
+            $crawler = $crawler->reduce(function (Crawler $node, $i) use ($content) {
+                if ($content === '') {
+                    return $node->text() === '';
+                }
+
+                if (preg_match('/^regexp\s*:\s*(.*)/i', $content, $matches)) {
+                    return (bool) preg_match($matches[1], $node->text());
+                }
+
+                return strstr($node->text(), $content) !== false;
+            });
+        }
+
+        $found = count($crawler);
+
+        if (is_numeric($count)) {
+            self::assertEquals($count, $found, $message);
+        }
+
+        else if (is_bool($count)) {
+            $found = $found > 0;
+
+            if ($count) {
+                self::assertTrue($found, $message);
+            } else {
+                self::assertFalse($found, $message);
+            }
+        }
+
+        else if (is_array($count) &&
+            (isset($count['>']) || isset($count['<']) ||
+            isset($count['>=']) || isset($count['<=']))) {
+
+            if (isset($count['>'])) {
+                self::assertTrue($found > $count['>'], $message);
+            }
+
+            if (isset($count['>='])) {
+                self::assertTrue($found >= $count['>='], $message);
+            }
+
+            if (isset($count['<'])) {
+                self::assertTrue($found < $count['<'], $message);
+            }
+
+            if (isset($count['<='])) {
+                self::assertTrue($found <= $count['<='], $message);
+            }
+        }
+
+        else {
+            throw new PHPUnit_Framework_Exception('Invalid count format');
+        }
+    }
+}
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/tests/DomTestCaseTest.php b/core/vendor/phpunit/phpunit-dom-assertions/tests/DomTestCaseTest.php
new file mode 100644
index 0000000..2d9cf49
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/tests/DomTestCaseTest.php
@@ -0,0 +1,1033 @@
+<?php
+/**
+ * PHPUnit
+ *
+ * Copyright (c) 2001-2014, Sebastian Bergmann <sebastian@phpunit.de>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @package    PHPUnit_Framework_DOMTestCase
+ * @author     Sebastian Bergmann <sebastian@phpunit.de>
+ * @author     Jeff Welch <whatthejeff@gmail.com>
+ * @copyright  2014 Sebastian Bergmann <sebastian@phpunit.de>
+ * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
+ * @link       http://github.com/phpunit/phpunit-dom-assertions
+ * @since      File available since Release 1.0.0
+ */
+
+/**
+ *
+ *
+ * @package    PHPUnit_Framework_DOMTestCase
+ * @author     Sebastian Bergmann <sebastian@phpunit.de>
+ * @author     Jeff Welch <whatthejeff@gmail.com>
+ * @copyright  2014 Sebastian Bergmann <sebastian@phpunit.de>
+ * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
+ * @link       http://github.com/phpunit/phpunit-dom-assertions
+ * @since      Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_DOMTestCaseTest extends PHPUnit_Framework_DOMTestCase
+{
+    private $html;
+
+    protected function setUp()
+    {
+        $this->html = file_get_contents(
+            __DIR__ . '/_files/SelectorAssertionsFixture.html'
+        );
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagTypeTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'html');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagTypeFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'code');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagIdTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_text');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagIdFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_text_doesnt_exist');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagStringContentTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_text',
+            'content' => 'My test tag content');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagStringContentFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_text',
+            'content' => 'My non existent tag content');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagRegexpContentTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_text',
+            'content' => 'regexp:/test tag/');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagRegexpModifierContentTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_text',
+            'content' => 'regexp:/TEST TAG/i');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagRegexpContentFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_text',
+            'content' => 'regexp:/asdf/');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagCdataContentTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'script',
+            'content' => 'alert(\'Hello, world!\');');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagCdataontentFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'script',
+            'content' => 'asdf');
+        $this->assertTag($matcher, $this->html);
+    }
+
+
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAttributesTrueA()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'span',
+            'attributes' => array('class' => 'test_class'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAttributesTrueB()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'attributes' => array('id' => 'test_child_id'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagAttributesFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'span',
+            'attributes' => array('class' => 'test_missing_class'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAttributesRegexpTrueA()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'span',
+            'attributes' => array('class' => 'regexp:/.+_class/'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAttributesRegexpTrueB()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'attributes' => array('id' => 'regexp:/.+_child_.+/'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAttributesRegexpModifierTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'attributes' => array('id' => 'regexp:/.+_CHILD_.+/i'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagAttributesRegexpModifierFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'attributes' => array('id' => 'regexp:/.+_CHILD_.+/'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagAttributesRegexpFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'span',
+            'attributes' => array('class' => 'regexp:/.+_missing_.+/'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAttributesMultiPartClassTrueA()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'id'  => 'test_multi_class',
+            'attributes' => array('class' => 'multi class'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAttributesMultiPartClassTrueB()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'id'  => 'test_multi_class',
+            'attributes' => array('class' => 'multi'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagAttributesMultiPartClassFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'id'  => 'test_multi_class',
+            'attributes' => array('class' => 'mul'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagParentTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'head',
+            'parent' => array('tag' => 'html'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagParentFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'head',
+            'parent' => array('tag' => 'div'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagMultiplePossibleChildren()
+    {
+        $this->markTestIncomplete();
+        $matcher = array(
+            'tag' => 'li',
+            'parent' => array(
+                'tag' => 'ul',
+                'id' => 'another_ul'
+            )
+        );
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagChildTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'html',
+            'child' => array('tag' => 'head'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagChildFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'html',
+            'child' => array('tag' => 'div'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAdjacentSiblingTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'img',
+            'adjacent-sibling' => array('tag' => 'input'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagAdjacentSiblingFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'img',
+            'adjacent-sibling' => array('tag' => 'div'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagAncestorTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'ancestor' => array('tag' => 'html'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagAncestorFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'html',
+            'ancestor' => array('tag' => 'div'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagDescendantTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'html',
+            'descendant' => array('tag' => 'div'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagDescendantFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'descendant' => array('tag' => 'html'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagChildrenCountTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('count' => 3));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagChildrenCountFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('count' => 5));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagChildrenLessThanTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('less_than' => 10));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagChildrenLessThanFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('less_than' => 2));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagChildrenGreaterThanTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('greater_than' => 2));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagChildrenGreaterThanFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('greater_than' => 10));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagChildrenOnlyTrue()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('only' => array('tag' =>'li')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagChildrenOnlyFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul',
+            'children' => array('only' => array('tag' =>'div')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagTypeIdTrueA()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'ul', 'id' => 'my_ul');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagTypeIdTrueB()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'my_ul', 'tag' => 'ul');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagTypeIdTrueC()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'input', 'id'  => 'input_test_id');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertTagTypeIdFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div', 'id'  => 'my_ul');
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertTagContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'content'    => 'Test Id Text',
+            'attributes' => array('id' => 'test_id',
+                'class' => 'my_test_class'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertParentContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag'        => 'div',
+            'content'    => 'Test Id Text',
+            'attributes' => array('id'    => 'test_id',
+                'class' => 'my_test_class'),
+            'parent'     => array('tag' => 'body'));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertChildContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag'        => 'div',
+            'content'    => 'Test Id Text',
+            'attributes' => array('id'    => 'test_id',
+                'class' => 'my_test_class'),
+            'child'      => array('tag'        => 'div',
+                'attributes' => array('id' => 'test_child_id')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertAdjacentSiblingContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag'              => 'div',
+            'content'          => 'Test Id Text',
+            'attributes'       => array('id'    => 'test_id',
+                'class' => 'my_test_class'),
+            'adjacent-sibling' => array('tag'        => 'div',
+                'attributes' => array('id' => 'test_children')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertChildSubChildren()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_id',
+            'child' => array('id' => 'test_child_id',
+                'child' => array('id' => 'test_subchild_id')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertAdjacentSiblingSubAdjacentSibling()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id' => 'test_id',
+            'adjacent-sibling' => array('id' => 'test_children',
+                'adjacent-sibling' => array('class' => 'test_class')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertAncestorContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id'         => 'test_subchild_id',
+            'content'    => 'My Subchild',
+            'attributes' => array('id' => 'test_subchild_id'),
+            'ancestor'   => array('tag'        => 'div',
+                'attributes' => array('id' => 'test_id')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertDescendantContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id'         => 'test_id',
+            'content'    => 'Test Id Text',
+            'attributes' => array('id'  => 'test_id'),
+            'descendant' => array('tag'        => 'span',
+                'attributes' => array('id' => 'test_subchild_id')));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertTag
+     */
+    public function testAssertChildrenContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('id'         => 'test_children',
+            'content'    => 'My Children',
+            'attributes' => array('class'  => 'children'),
+
+            'children' => array('less_than'    => '25',
+                'greater_than' => '2',
+                'only'         => array('tag' => 'div',
+                    'attributes' => array('class' => 'my_child'))
+            ));
+        $this->assertTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertNotTag
+     */
+    public function testAssertNotTagTypeIdFalse()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div', 'id'  => 'my_ul');
+        $this->assertNotTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertNotTag
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertNotTagContentAttributes()
+    {
+        $this->markTestIncomplete();
+        $matcher = array('tag' => 'div',
+            'content'    => 'Test Id Text',
+            'attributes' => array('id' => 'test_id',
+                'class' => 'my_test_class'));
+        $this->assertNotTag($matcher, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountPresentTrue()
+    {
+        $selector = 'div#test_id';
+        $count    = true;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountPresentFalse()
+    {
+        $selector = 'div#non_existent';
+        $count    = true;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountNotPresentTrue()
+    {
+        $selector = 'div#non_existent';
+        $count    = false;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectNotPresentFalse()
+    {
+        $selector = 'div#test_id';
+        $count    = false;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountChildTrue()
+    {
+        $selector = '#my_ul > li';
+        $count    = 3;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountChildFalse()
+    {
+        $selector = '#my_ul > li';
+        $count    = 4;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountAdjacentSiblingTrue()
+    {
+        $selector = 'div + div + div';
+        $count    = 2;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountAdjacentSiblingFalse()
+    {
+        $selector = '#test_children + div';
+        $count    = 1;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountDescendantTrue()
+    {
+        $selector = '#my_ul li';
+        $count    = 3;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountDescendantFalse()
+    {
+        $selector = '#my_ul li';
+        $count    = 4;
+
+        $this->assertSelectCount($selector, $count, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountGreaterThanTrue()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('>' => 2);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountGreaterThanFalse()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('>' => 3);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountGreaterThanEqualToTrue()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('>=' => 3);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountGreaterThanEqualToFalse()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('>=' => 4);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountLessThanTrue()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('<' => 4);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountLessThanFalse()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('<' => 3);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountLessThanEqualToTrue()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('<=' => 3);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountLessThanEqualToFalse()
+    {
+        $selector = '#my_ul > li';
+        $range  = array('<=' => 2);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectCount
+     */
+    public function testAssertSelectCountRangeTrue()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('>' => 2, '<' => 4);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectCount
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectCountRangeFalse()
+    {
+        $selector = '#my_ul > li';
+        $range    = array('>' => 1, '<' => 3);
+
+        $this->assertSelectCount($selector, $range, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectEquals
+     */
+    public function testAssertSelectEqualsContentPresentTrue()
+    {
+        $selector = 'span.test_class';
+        $content  = 'Test Class Text';
+
+        $this->assertSelectEquals($selector, $content, true, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectEquals
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectEqualsContentPresentFalse()
+    {
+        $selector = 'span.test_class';
+        $content  = 'Test Nonexistent';
+
+        $this->assertSelectEquals($selector, $content, true, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectEquals
+     */
+    public function testAssertSelectEqualsContentNotPresentTrue()
+    {
+        $selector = 'span.test_class';
+        $content  = 'Test Nonexistent';
+
+        $this->assertSelectEquals($selector, $content, false, $this->html);
+    }
+
+    /**
+     * @covers            PHPUnit_Framework_DOMTestCase::assertSelectEquals
+     * @expectedException PHPUnit_Framework_AssertionFailedError
+     */
+    public function testAssertSelectEqualsContentNotPresentFalse()
+    {
+        $selector = 'span.test_class';
+        $content  = 'Test Class Text';
+
+        $this->assertSelectEquals($selector, $content, false, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectRegExp
+     */
+    public function testAssertSelectRegExpContentPresentTrue()
+    {
+        $selector = 'span.test_class';
+        $regexp   = '/Test.*Text/';
+
+        $this->assertSelectRegExp($selector, $regexp, true, $this->html);
+    }
+
+    /**
+     * @covers PHPUnit_Framework_DOMTestCase::assertSelectRegExp
+     */
+    public function testAssertSelectRegExpContentPresentFalse()
+    {
+        $selector = 'span.test_class';
+        $regexp   = '/Nonexistant/';
+
+        $this->assertSelectRegExp($selector, $regexp, false, $this->html);
+    }
+}
diff --git a/core/vendor/phpunit/phpunit-dom-assertions/tests/_files/SelectorAssertionsFixture.html b/core/vendor/phpunit/phpunit-dom-assertions/tests/_files/SelectorAssertionsFixture.html
new file mode 100644
index 0000000..41256da
--- /dev/null
+++ b/core/vendor/phpunit/phpunit-dom-assertions/tests/_files/SelectorAssertionsFixture.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+  <title>Login</title>
+  <link type="text/css" rel="Stylesheet" href="/stylesheets/screen.css">
+  <script type="text/javascript">alert('Hello, world!');</script>
+ </head>
+ <body id="login">
+  <ul id="my_ul" class="my_ul_class">
+    <li class="my_li">Test LI 1</li>
+    <li class="my_li">Test LI 2</li>
+    <li class="my_li">Test LI 3</li>
+  </ul>
+  <ul id="another_ul" class="my_ul_class">
+    <li class="my_li">Test LI 4</li>
+  </ul>
+  <div id="test_id" class="my_test_class">
+    <div id="test_child_id">
+      <span id="test_subchild_id">My Subchild</span>
+      My Child
+    </div>
+
+    Test Id Text
+  </div>
+  <div id="test_children" class="children">
+    My Children
+    <div class="my_child">1</div>
+    <div class="my_child">2</div>
+    <div class="my_child">3</div>
+    <div class="my_child">4</div>
+  </div>
+  <span class="test_class">
+    Test Class Text
+  </span>
+  <img src="/images/login_logo.gif" alt="Login Logo">
+  <input id="input_test_id" type="text" class="full">
+  <div id="test_text">
+    My test tag content
+    <div class="another_node">more text</div>
+  </div>
+  <div id="test_multi_class" class="multi part class">test</div>
+ </body>
+</html>
diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/.gitignore b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
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 100755
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 100755
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>
