Browse Source

Tool - Add packer for php-html-css-js

DSR! 4 years ago
parent
commit
72280a1c45
100 changed files with 8627 additions and 0 deletions
  1. 243 0
      packer/deps/PackPHP.php
  2. 6 0
      packer/deps/composer.json
  3. 363 0
      packer/deps/composer.lock
  4. 7 0
      packer/deps/vendor/autoload.php
  5. 14 0
      packer/deps/vendor/bin/minifycss
  6. 4 0
      packer/deps/vendor/bin/minifycss.bat
  7. 14 0
      packer/deps/vendor/bin/minifyjs
  8. 4 0
      packer/deps/vendor/bin/minifyjs.bat
  9. 477 0
      packer/deps/vendor/composer/ClassLoader.php
  10. 329 0
      packer/deps/vendor/composer/InstalledVersions.php
  11. 21 0
      packer/deps/vendor/composer/LICENSE
  12. 10 0
      packer/deps/vendor/composer/autoload_classmap.php
  13. 9 0
      packer/deps/vendor/composer/autoload_namespaces.php
  14. 14 0
      packer/deps/vendor/composer/autoload_psr4.php
  15. 57 0
      packer/deps/vendor/composer/autoload_real.php
  16. 62 0
      packer/deps/vendor/composer/autoload_static.php
  17. 365 0
      packer/deps/vendor/composer/installed.json
  18. 69 0
      packer/deps/vendor/composer/installed.php
  19. 26 0
      packer/deps/vendor/composer/platform_check.php
  20. 12 0
      packer/deps/vendor/matthiasmullie/minify/.github/FUNDING.yml
  21. 59 0
      packer/deps/vendor/matthiasmullie/minify/CONTRIBUTING.md
  22. 13 0
      packer/deps/vendor/matthiasmullie/minify/Dockerfile
  23. 18 0
      packer/deps/vendor/matthiasmullie/minify/LICENSE
  24. 45 0
      packer/deps/vendor/matthiasmullie/minify/bin/minifycss
  25. 45 0
      packer/deps/vendor/matthiasmullie/minify/bin/minifyjs
  26. 38 0
      packer/deps/vendor/matthiasmullie/minify/composer.json
  27. 7 0
      packer/deps/vendor/matthiasmullie/minify/data/js/keywords_after.txt
  28. 26 0
      packer/deps/vendor/matthiasmullie/minify/data/js/keywords_before.txt
  29. 63 0
      packer/deps/vendor/matthiasmullie/minify/data/js/keywords_reserved.txt
  30. 46 0
      packer/deps/vendor/matthiasmullie/minify/data/js/operators.txt
  31. 43 0
      packer/deps/vendor/matthiasmullie/minify/data/js/operators_after.txt
  32. 43 0
      packer/deps/vendor/matthiasmullie/minify/data/js/operators_before.txt
  33. 46 0
      packer/deps/vendor/matthiasmullie/minify/docker-compose.yml
  34. 786 0
      packer/deps/vendor/matthiasmullie/minify/src/CSS.php
  35. 20 0
      packer/deps/vendor/matthiasmullie/minify/src/Exception.php
  36. 23 0
      packer/deps/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php
  37. 21 0
      packer/deps/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php
  38. 21 0
      packer/deps/vendor/matthiasmullie/minify/src/Exceptions/IOException.php
  39. 33 0
      packer/deps/vendor/matthiasmullie/minify/src/JS.php
  40. 501 0
      packer/deps/vendor/matthiasmullie/minify/src/Minify.php
  41. 18 0
      packer/deps/vendor/matthiasmullie/path-converter/LICENSE
  42. 28 0
      packer/deps/vendor/matthiasmullie/path-converter/composer.json
  43. 204 0
      packer/deps/vendor/matthiasmullie/path-converter/src/Converter.php
  44. 24 0
      packer/deps/vendor/matthiasmullie/path-converter/src/ConverterInterface.php
  45. 23 0
      packer/deps/vendor/matthiasmullie/path-converter/src/NoConverter.php
  46. 18 0
      packer/deps/vendor/symfony/css-selector/CHANGELOG.md
  47. 69 0
      packer/deps/vendor/symfony/css-selector/CssSelectorConverter.php
  48. 24 0
      packer/deps/vendor/symfony/css-selector/Exception/ExceptionInterface.php
  49. 24 0
      packer/deps/vendor/symfony/css-selector/Exception/ExpressionErrorException.php
  50. 24 0
      packer/deps/vendor/symfony/css-selector/Exception/InternalErrorException.php
  51. 24 0
      packer/deps/vendor/symfony/css-selector/Exception/ParseException.php
  52. 65 0
      packer/deps/vendor/symfony/css-selector/Exception/SyntaxErrorException.php
  53. 19 0
      packer/deps/vendor/symfony/css-selector/LICENSE
  54. 39 0
      packer/deps/vendor/symfony/css-selector/Node/AbstractNode.php
  55. 85 0
      packer/deps/vendor/symfony/css-selector/Node/AttributeNode.php
  56. 60 0
      packer/deps/vendor/symfony/css-selector/Node/ClassNode.php
  57. 69 0
      packer/deps/vendor/symfony/css-selector/Node/CombinedSelectorNode.php
  58. 62 0
      packer/deps/vendor/symfony/css-selector/Node/ElementNode.php
  59. 79 0
      packer/deps/vendor/symfony/css-selector/Node/FunctionNode.php
  60. 60 0
      packer/deps/vendor/symfony/css-selector/Node/HashNode.php
  61. 60 0
      packer/deps/vendor/symfony/css-selector/Node/NegationNode.php
  62. 31 0
      packer/deps/vendor/symfony/css-selector/Node/NodeInterface.php
  63. 60 0
      packer/deps/vendor/symfony/css-selector/Node/PseudoNode.php
  64. 60 0
      packer/deps/vendor/symfony/css-selector/Node/SelectorNode.php
  65. 73 0
      packer/deps/vendor/symfony/css-selector/Node/Specificity.php
  66. 48 0
      packer/deps/vendor/symfony/css-selector/Parser/Handler/CommentHandler.php
  67. 30 0
      packer/deps/vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php
  68. 58 0
      packer/deps/vendor/symfony/css-selector/Parser/Handler/HashHandler.php
  69. 58 0
      packer/deps/vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php
  70. 54 0
      packer/deps/vendor/symfony/css-selector/Parser/Handler/NumberHandler.php
  71. 77 0
      packer/deps/vendor/symfony/css-selector/Parser/Handler/StringHandler.php
  72. 46 0
      packer/deps/vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.php
  73. 353 0
      packer/deps/vendor/symfony/css-selector/Parser/Parser.php
  74. 34 0
      packer/deps/vendor/symfony/css-selector/Parser/ParserInterface.php
  75. 86 0
      packer/deps/vendor/symfony/css-selector/Parser/Reader.php
  76. 51 0
      packer/deps/vendor/symfony/css-selector/Parser/Shortcut/ClassParser.php
  77. 47 0
      packer/deps/vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php
  78. 46 0
      packer/deps/vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php
  79. 51 0
      packer/deps/vendor/symfony/css-selector/Parser/Shortcut/HashParser.php
  80. 111 0
      packer/deps/vendor/symfony/css-selector/Parser/Token.php
  81. 171 0
      packer/deps/vendor/symfony/css-selector/Parser/TokenStream.php
  82. 73 0
      packer/deps/vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php
  83. 65 0
      packer/deps/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php
  84. 89 0
      packer/deps/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php
  85. 20 0
      packer/deps/vendor/symfony/css-selector/README.md
  86. 65 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php
  87. 119 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php
  88. 71 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php
  89. 67 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php
  90. 171 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php
  91. 187 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php
  92. 197 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/NodeExtension.php
  93. 122 0
      packer/deps/vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php
  94. 230 0
      packer/deps/vendor/symfony/css-selector/XPath/Translator.php
  95. 37 0
      packer/deps/vendor/symfony/css-selector/XPath/TranslatorInterface.php
  96. 102 0
      packer/deps/vendor/symfony/css-selector/XPath/XPathExpr.php
  97. 32 0
      packer/deps/vendor/symfony/css-selector/composer.json
  98. 215 0
      packer/deps/vendor/voku/html-min/CHANGELOG.md
  99. 21 0
      packer/deps/vendor/voku/html-min/LICENSE
  100. 118 0
      packer/deps/vendor/voku/html-min/README.md

+ 243 - 0
packer/deps/PackPHP.php

@@ -0,0 +1,243 @@
+<?php
+
+/*
+ * Minify PHP source
+ * Based on algorithm by gelamu(ät)gmail(dt)com
+ * http://php.net/manual/fr/function.php-strip-whitespace.php
+ * 
+ * @package Packer
+ * @author Vallo Reima
+ * @copyright (C)2015
+ */
+
+class PackPHP {
+
+  private $obn = null;  /* obfuscation object */
+  private $fle = '';    /* source file name */
+  private $min = true;  /* pack flag */
+  private static $IW = [ /* Whitespaces left and right from this signs can be ignored */
+      T_CONCAT_EQUAL, // .=
+      T_DOUBLE_ARROW, // =>
+      T_BOOLEAN_AND, // &&
+      T_BOOLEAN_OR, // ||
+      T_IS_EQUAL, // ==
+      T_IS_NOT_EQUAL, // != or <>
+      T_IS_SMALLER_OR_EQUAL, // <=
+      T_IS_GREATER_OR_EQUAL, // >=
+      T_INC, // ++
+      T_DEC, // --
+      T_PLUS_EQUAL, // +=
+      T_MINUS_EQUAL, // -=
+      T_MUL_EQUAL, // *=
+      T_DIV_EQUAL, // /=
+      T_IS_IDENTICAL, // ===
+      T_IS_NOT_IDENTICAL, // !==
+      T_DOUBLE_COLON, // ::
+      T_PAAMAYIM_NEKUDOTAYIM, // ::
+      T_OBJECT_OPERATOR, // ->
+      T_DOLLAR_OPEN_CURLY_BRACES, // ${
+      T_AND_EQUAL, // &=
+      T_MOD_EQUAL, // %=
+      T_XOR_EQUAL, // ^=
+      T_OR_EQUAL, // |=
+      T_SL, // <<
+      T_SR, // >>
+      T_SL_EQUAL, // <<=
+      T_SR_EQUAL, // >>=
+  ];
+
+  /**
+   * @param string $source
+   * @param array $options
+   * @return mixed -- string - ok
+   */
+  public static function minify($source, $options = []) {
+    $min = new self($options);
+    return $min->process($source);
+  }
+
+  /**
+   * save options
+   * @param array $opts
+   */
+  public function __construct($opts) {
+    if (isset($opts['min'])) {
+      $this->min = $opts['min'] !== false; // compress yes/no
+    }
+    if (isset($opts['obn']) && !empty($opts['fle'])) {
+      $this->obn = $opts['obn']; //obfuscator call
+      $this->fle = $opts['fle']; //filename to save
+    }
+  }
+
+  /**
+   * compress code and register identifiers
+   * @param string $php source
+   * @return array
+   */
+  private function process($php) {
+    $flg = $this->obn ? false : null; //php identifiers' collection
+    if (mb_stripos(pathinfo($this->fle, PATHINFO_EXTENSION), 'php') !== 0) {//not php file
+      $php = $this->Fix($php); //04.2017
+    }
+    $rlt = $this->Compress($php, $flg);
+    if (!$this->min) {
+      $rlt = $php;  // return not-packed
+    }
+    if ($flg) { // source has php tags
+      call_user_func($this->obn, -1, $this->fle); // save filename
+    }
+    return $rlt;
+  }
+
+  /**
+   * add a space if missing after the tag
+   * @param string $src
+   * @return string
+   */
+  private function Fix($src) {
+    $t = '<?php';
+    $l = strlen($t);
+    $s = '';
+    $n = mb_strlen($src);
+    $i = 0;
+    while ($i < $n) {
+      $k = mb_stripos($src, $t, $i);
+      if ($k === false) {
+        $s .= mb_substr($src, $i, $n - $i);
+        $i = $n;
+      } else {//tag found
+        $k += $l;
+        $s .= mb_substr($src, $i, $k - $i);
+        $i = $k;
+        if ($i < $n && mb_substr($src, $i, 1) !== ' ') {
+          $s .= ' ';//insert missing space
+        }
+      }
+    }
+    return $s;
+  }
+
+  /**
+   * compress PHP source code
+   * @param string $src
+   * @param mixed $flg -- false - fix open tag
+   *                      null - no check
+   * @return string
+   */
+  private function Compress($src, &$flg) {
+    $tokens = token_get_all($src);
+    $new = "";
+    $c = sizeof($tokens);
+    $iw = false; // ignore whitespace
+    $ls = "";    // last sign
+    $ot = null;  // open tag
+    for ($i = 0; $i < $c; $i++) {
+      $token = $tokens[$i];
+      if (is_array($token)) {
+        list($tn, $ts) = $token; // tokens: number, string, line
+        if ($tn == T_INLINE_HTML) { // token_name($tn)
+          $new .= $ts;
+          $iw = false;
+        } else {
+          if ($tn == T_OPEN_TAG) {
+            if (strpos($ts, " ") || strpos($ts, "\n") || strpos($ts, "\t") || strpos($ts, "\r")) {
+              $ts = rtrim($ts);
+            }
+            $ts .= " ";
+            $new .= $ts;
+            $ot = T_OPEN_TAG;
+            $iw = true;
+          } elseif ($tn == T_OPEN_TAG_WITH_ECHO) {
+            $new .= $ts;
+            $ot = T_OPEN_TAG_WITH_ECHO;
+            $iw = true;
+          } elseif ($tn == T_CLOSE_TAG) {
+            if ($ot == T_OPEN_TAG_WITH_ECHO) {
+              $new = rtrim($new, "; ");
+            } else {
+              $ts = " " . $ts;
+            }
+            $new .= $ts;
+            $ot = null;
+            $iw = false;
+          } elseif (in_array($tn, self::$IW)) {
+            $new .= $ts;
+            $iw = true;
+          } elseif ($tn == T_CONSTANT_ENCAPSED_STRING || $tn == T_ENCAPSED_AND_WHITESPACE) {
+            /*            if ($ts[0] == '"') {
+              $ts = addcslashes($ts, "\n\t\r"); //VR
+              } */
+            $new .= $ts;
+            $iw = true;
+          } elseif ($tn == T_WHITESPACE) {
+            $nt = @$tokens[$i + 1];
+            if (!$iw && (!is_string($nt) || $nt == '$') && !in_array($nt[0], self::$IW)) {
+              $new .= " ";
+            }
+            $iw = false;
+          } elseif ($tn == T_END_HEREDOC) {
+            $new .= "$ts;\n"; //VR add newline
+            $iw = true;
+            for ($j = $i + 1; $j < $c; $j++) {
+              if (is_string($tokens[$j]) && $tokens[$j] == ";") {
+                $i = $j;
+                break;
+              } else if ($tokens[$j][0] == T_CLOSE_TAG) {
+                break;
+              }
+            }
+          } elseif ($tn == T_COMMENT || $tn == T_DOC_COMMENT) {
+            $iw = true;
+          } else {
+            if ($tn == T_START_HEREDOC && !mb_strpos($ts, "\n")) { //VR check newline
+              $ts .= "\n";
+            }
+            $new .= $ts;
+            $iw = false;
+            if (!is_null($flg)) {//VR identifier registration
+              call_user_func($this->obn, $i, $tokens); //save possible identifier
+              $flg = !is_null($ot) || $flg; // mark php content
+            }
+          }
+        }
+        $ls = "";
+      } else {
+        if (($token == ";" || $token == ":") && $ls == $token) {
+          $new .= $this->Double($tokens, $i); //VR
+        } else {
+          $new .= $token;
+          $ls = $token;
+        }
+        $iw = true;
+      }
+    }
+    return $new;
+  }
+
+  /**
+   * check to omit doubled character
+   * @param array $tks tokens
+   * @param int $i current token index
+   * @return string append to output
+   */
+  private function Double($tks, $i) {
+    $r = '';
+    if ($tks[$i] == ';') {
+      $j = $i - 1;
+      while ($j > 2 && $tks[$j] != '(') { // find condition beginning
+        $j--;
+      }
+      if ($j > 2) {
+        if (is_array($tks[$j - 1]) && $tks[$j - 1][0] == T_WHITESPACE) {
+          $j--;
+        }
+        if (is_array($tks[$j - 1]) && $tks[$j - 1][0] == T_FOR) {
+          $r = $tks[$i];  // for (;;)
+        }
+      }
+    }
+    return $r;
+  }
+
+}

+ 6 - 0
packer/deps/composer.json

@@ -0,0 +1,6 @@
+{
+    "require": {
+        "matthiasmullie/minify": "^1.3",
+        "voku/html-min": "^4.4"
+    }
+}

+ 363 - 0
packer/deps/composer.lock

@@ -0,0 +1,363 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "5ed24536d10d3986f4e683a4aa19c661",
+    "packages": [
+        {
+            "name": "matthiasmullie/minify",
+            "version": "1.3.66",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/matthiasmullie/minify.git",
+                "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/45fd3b0f1dfa2c965857c6d4a470bea52adc31a6",
+                "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "matthiasmullie/path-converter": "~1.1",
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "~2.0",
+                "matthiasmullie/scrapbook": "dev-master",
+                "phpunit/phpunit": ">=4.8"
+            },
+            "suggest": {
+                "psr/cache-implementation": "Cache implementation to use with Minify::cache"
+            },
+            "bin": [
+                "bin/minifycss",
+                "bin/minifyjs"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "MatthiasMullie\\Minify\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Matthias Mullie",
+                    "email": "minify@mullie.eu",
+                    "homepage": "http://www.mullie.eu",
+                    "role": "Developer"
+                }
+            ],
+            "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
+            "homepage": "http://www.minifier.org",
+            "keywords": [
+                "JS",
+                "css",
+                "javascript",
+                "minifier",
+                "minify"
+            ],
+            "support": {
+                "issues": "https://github.com/matthiasmullie/minify/issues",
+                "source": "https://github.com/matthiasmullie/minify/tree/1.3.66"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/[user1",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/user2",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-01-06T15:18:10+00:00"
+        },
+        {
+            "name": "matthiasmullie/path-converter",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/matthiasmullie/path-converter.git",
+                "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9",
+                "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "MatthiasMullie\\PathConverter\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Matthias Mullie",
+                    "email": "pathconverter@mullie.eu",
+                    "homepage": "http://www.mullie.eu",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Relative path converter",
+            "homepage": "http://github.com/matthiasmullie/path-converter",
+            "keywords": [
+                "converter",
+                "path",
+                "paths",
+                "relative"
+            ],
+            "support": {
+                "issues": "https://github.com/matthiasmullie/path-converter/issues",
+                "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3"
+            },
+            "time": "2019-02-05T23:41:09+00:00"
+        },
+        {
+            "name": "symfony/css-selector",
+            "version": "v5.2.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/css-selector.git",
+                "reference": "f65f217b3314504a1ec99c2d6ef69016bb13490f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/f65f217b3314504a1ec99c2d6ef69016bb13490f",
+                "reference": "f65f217b3314504a1ec99c2d6ef69016bb13490f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\CssSelector\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Jean-François Simon",
+                    "email": "jeanfrancois.simon@sensiolabs.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Converts CSS selectors to XPath expressions",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/css-selector/tree/v5.2.4"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T10:01:46+00:00"
+        },
+        {
+            "name": "voku/html-min",
+            "version": "4.4.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/HtmlMin.git",
+                "reference": "4f700584abd70b308b7d06b8e4cfcc31711faaf9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/HtmlMin/zipball/4f700584abd70b308b7d06b8e4cfcc31711faaf9",
+                "reference": "4f700584abd70b308b7d06b8e4cfcc31711faaf9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "php": ">=7.0.0",
+                "voku/simple_html_dom": "~4.7.23"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6.0 || ~7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "voku\\": "src/voku/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "http://www.moelleken.org/"
+                }
+            ],
+            "description": "HTML Compressor and Minifier",
+            "homepage": "https://github.com/voku/HtmlMin",
+            "keywords": [
+                "compress",
+                "compression",
+                "compressor",
+                "html",
+                "minifier"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/HtmlMin/issues",
+                "source": "https://github.com/voku/HtmlMin/tree/master"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2020-08-11T22:13:23+00:00"
+        },
+        {
+            "name": "voku/simple_html_dom",
+            "version": "4.7.29",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/simple_html_dom.git",
+                "reference": "079067c704b714b7c2813971297bb340307813e7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/simple_html_dom/zipball/079067c704b714b7c2813971297bb340307813e7",
+                "reference": "079067c704b714b7c2813971297bb340307813e7",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "ext-simplexml": "*",
+                "php": ">=7.0.0",
+                "symfony/css-selector": "~3.0 || ~4.0 || ~5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+            },
+            "suggest": {
+                "voku/portable-utf8": "If you need e.g. UTF-8 fixed output."
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "voku\\helper\\": "src/voku/helper/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "dimabdc",
+                    "email": "support@titor.ru",
+                    "homepage": "http://github.com/dimabdc",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "http://www.moelleken.org/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Simple HTML DOM package.",
+            "homepage": "http://simplehtmldom.sourceforge.net/",
+            "keywords": [
+                "HTML Parser",
+                "dom",
+                "php dom"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/simple_html_dom/issues",
+                "source": "https://github.com/voku/simple_html_dom/tree/4.7.29"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/voku/simple_html_dom",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-03-29T14:56:56+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": [],
+    "plugin-api-version": "2.0.0"
+}

+ 7 - 0
packer/deps/vendor/autoload.php

@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit81f7247027640df9f1bc96b2dd23a25f::getLoader();

+ 14 - 0
packer/deps/vendor/bin/minifycss

@@ -0,0 +1,14 @@
+#!/usr/bin/env sh
+
+dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../matthiasmullie/minify/bin" && pwd)
+
+if [ -d /proc/cygdrive ]; then
+    case $(which php) in
+        $(readlink -n /proc/cygdrive)/*)
+            # We are in Cygwin using Windows php, so the path must be translated
+            dir=$(cygpath -m "$dir");
+            ;;
+    esac
+fi
+
+"${dir}/minifycss" "$@"

+ 4 - 0
packer/deps/vendor/bin/minifycss.bat

@@ -0,0 +1,4 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/../matthiasmullie/minify/bin/minifycss
+php "%BIN_TARGET%" %*

+ 14 - 0
packer/deps/vendor/bin/minifyjs

@@ -0,0 +1,14 @@
+#!/usr/bin/env sh
+
+dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../matthiasmullie/minify/bin" && pwd)
+
+if [ -d /proc/cygdrive ]; then
+    case $(which php) in
+        $(readlink -n /proc/cygdrive)/*)
+            # We are in Cygwin using Windows php, so the path must be translated
+            dir=$(cygpath -m "$dir");
+            ;;
+    esac
+fi
+
+"${dir}/minifyjs" "$@"

+ 4 - 0
packer/deps/vendor/bin/minifyjs.bat

@@ -0,0 +1,4 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/../matthiasmullie/minify/bin/minifyjs
+php "%BIN_TARGET%" %*

+ 477 - 0
packer/deps/vendor/composer/ClassLoader.php

@@ -0,0 +1,477 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    https://www.php-fig.org/psr/psr-0/
+ * @see    https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    private $vendorDir;
+
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+    private $classMapAuthoritative = false;
+    private $missingClasses = array();
+    private $apcuPrefix;
+
+    private static $registeredLoaders = array();
+
+    public function __construct($vendorDir = null)
+    {
+        $this->vendorDir = $vendorDir;
+    }
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-4 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+        if (null === $this->vendorDir) {
+            //no-op
+        } elseif ($prepend) {
+            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+        } else {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+            self::$registeredLoaders[$this->vendorDir] = $this;
+        }
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+
+        if (null !== $this->vendorDir) {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+        }
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    /**
+     * Returns the currently registered loaders indexed by their corresponding vendor directories.
+     *
+     * @return self[]
+     */
+    public static function getRegisteredLoaders()
+    {
+        return self::$registeredLoaders;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath . '\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}

+ 329 - 0
packer/deps/vendor/composer/InstalledVersions.php

@@ -0,0 +1,329 @@
+<?php
+
+
+
+
+
+
+
+
+
+
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+
+
+
+
+
+class InstalledVersions
+{
+private static $installed = array (
+  'root' => 
+  array (
+    'pretty_version' => 'dev-master',
+    'version' => 'dev-master',
+    'aliases' => 
+    array (
+    ),
+    'reference' => 'ea843e061f44cf14125fc3280f232b55f34e1573',
+    'name' => '__root__',
+  ),
+  'versions' => 
+  array (
+    '__root__' => 
+    array (
+      'pretty_version' => 'dev-master',
+      'version' => 'dev-master',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'ea843e061f44cf14125fc3280f232b55f34e1573',
+    ),
+    'matthiasmullie/minify' => 
+    array (
+      'pretty_version' => '1.3.66',
+      'version' => '1.3.66.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
+    ),
+    'matthiasmullie/path-converter' => 
+    array (
+      'pretty_version' => '1.1.3',
+      'version' => '1.1.3.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
+    ),
+    'symfony/css-selector' => 
+    array (
+      'pretty_version' => 'v5.2.4',
+      'version' => '5.2.4.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'f65f217b3314504a1ec99c2d6ef69016bb13490f',
+    ),
+    'voku/html-min' => 
+    array (
+      'pretty_version' => '4.4.8',
+      'version' => '4.4.8.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '4f700584abd70b308b7d06b8e4cfcc31711faaf9',
+    ),
+    'voku/simple_html_dom' => 
+    array (
+      'pretty_version' => '4.7.29',
+      'version' => '4.7.29.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '079067c704b714b7c2813971297bb340307813e7',
+    ),
+  ),
+);
+private static $canGetVendors;
+private static $installedByVendor = array();
+
+
+
+
+
+
+
+public static function getInstalledPackages()
+{
+$packages = array();
+foreach (self::getInstalled() as $installed) {
+$packages[] = array_keys($installed['versions']);
+}
+
+
+if (1 === \count($packages)) {
+return $packages[0];
+}
+
+return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+}
+
+
+
+
+
+
+
+
+
+public static function isInstalled($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (isset($installed['versions'][$packageName])) {
+return true;
+}
+}
+
+return false;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function satisfies(VersionParser $parser, $packageName, $constraint)
+{
+$constraint = $parser->parseConstraints($constraint);
+$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+return $provided->matches($constraint);
+}
+
+
+
+
+
+
+
+
+
+
+public static function getVersionRanges($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+$ranges = array();
+if (isset($installed['versions'][$packageName]['pretty_version'])) {
+$ranges[] = $installed['versions'][$packageName]['pretty_version'];
+}
+if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+}
+if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+}
+if (array_key_exists('provided', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+}
+
+return implode(' || ', $ranges);
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getVersion($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['version'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['version'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getPrettyVersion($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['pretty_version'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getReference($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['reference'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['reference'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getRootPackage()
+{
+$installed = self::getInstalled();
+
+return $installed[0]['root'];
+}
+
+
+
+
+
+
+
+public static function getRawData()
+{
+return self::$installed;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function reload($data)
+{
+self::$installed = $data;
+self::$installedByVendor = array();
+}
+
+
+
+
+private static function getInstalled()
+{
+if (null === self::$canGetVendors) {
+self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+}
+
+$installed = array();
+
+if (self::$canGetVendors) {
+
+foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+if (isset(self::$installedByVendor[$vendorDir])) {
+$installed[] = self::$installedByVendor[$vendorDir];
+} elseif (is_file($vendorDir.'/composer/installed.php')) {
+$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+}
+}
+}
+
+$installed[] = self::$installed;
+
+return $installed;
+}
+}

+ 21 - 0
packer/deps/vendor/composer/LICENSE

@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+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.
+

+ 10 - 0
packer/deps/vendor/composer/autoload_classmap.php

@@ -0,0 +1,10 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+);

+ 9 - 0
packer/deps/vendor/composer/autoload_namespaces.php

@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);

+ 14 - 0
packer/deps/vendor/composer/autoload_psr4.php

@@ -0,0 +1,14 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'voku\\helper\\' => array($vendorDir . '/voku/simple_html_dom/src/voku/helper'),
+    'voku\\' => array($vendorDir . '/voku/html-min/src/voku'),
+    'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
+    'MatthiasMullie\\PathConverter\\' => array($vendorDir . '/matthiasmullie/path-converter/src'),
+    'MatthiasMullie\\Minify\\' => array($vendorDir . '/matthiasmullie/minify/src'),
+);

+ 57 - 0
packer/deps/vendor/composer/autoload_real.php

@@ -0,0 +1,57 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit81f7247027640df9f1bc96b2dd23a25f
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    /**
+     * @return \Composer\Autoload\ClassLoader
+     */
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        require __DIR__ . '/platform_check.php';
+
+        spl_autoload_register(array('ComposerAutoloaderInit81f7247027640df9f1bc96b2dd23a25f', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
+        spl_autoload_unregister(array('ComposerAutoloaderInit81f7247027640df9f1bc96b2dd23a25f', 'loadClassLoader'));
+
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+        if ($useStaticLoader) {
+            require __DIR__ . '/autoload_static.php';
+
+            call_user_func(\Composer\Autoload\ComposerStaticInit81f7247027640df9f1bc96b2dd23a25f::getInitializer($loader));
+        } else {
+            $map = require __DIR__ . '/autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                $loader->set($namespace, $path);
+            }
+
+            $map = require __DIR__ . '/autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                $loader->setPsr4($namespace, $path);
+            }
+
+            $classMap = require __DIR__ . '/autoload_classmap.php';
+            if ($classMap) {
+                $loader->addClassMap($classMap);
+            }
+        }
+
+        $loader->register(true);
+
+        return $loader;
+    }
+}

+ 62 - 0
packer/deps/vendor/composer/autoload_static.php

@@ -0,0 +1,62 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit81f7247027640df9f1bc96b2dd23a25f
+{
+    public static $prefixLengthsPsr4 = array (
+        'v' => 
+        array (
+            'voku\\helper\\' => 12,
+            'voku\\' => 5,
+        ),
+        'S' => 
+        array (
+            'Symfony\\Component\\CssSelector\\' => 30,
+        ),
+        'M' => 
+        array (
+            'MatthiasMullie\\PathConverter\\' => 29,
+            'MatthiasMullie\\Minify\\' => 22,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'voku\\helper\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/voku/simple_html_dom/src/voku/helper',
+        ),
+        'voku\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/voku/html-min/src/voku',
+        ),
+        'Symfony\\Component\\CssSelector\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/css-selector',
+        ),
+        'MatthiasMullie\\PathConverter\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/matthiasmullie/path-converter/src',
+        ),
+        'MatthiasMullie\\Minify\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/matthiasmullie/minify/src',
+        ),
+    );
+
+    public static $classMap = array (
+        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInit81f7247027640df9f1bc96b2dd23a25f::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit81f7247027640df9f1bc96b2dd23a25f::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInit81f7247027640df9f1bc96b2dd23a25f::$classMap;
+
+        }, null, ClassLoader::class);
+    }
+}

+ 365 - 0
packer/deps/vendor/composer/installed.json

@@ -0,0 +1,365 @@
+{
+    "packages": [
+        {
+            "name": "matthiasmullie/minify",
+            "version": "1.3.66",
+            "version_normalized": "1.3.66.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/matthiasmullie/minify.git",
+                "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/45fd3b0f1dfa2c965857c6d4a470bea52adc31a6",
+                "reference": "45fd3b0f1dfa2c965857c6d4a470bea52adc31a6",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "matthiasmullie/path-converter": "~1.1",
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "~2.0",
+                "matthiasmullie/scrapbook": "dev-master",
+                "phpunit/phpunit": ">=4.8"
+            },
+            "suggest": {
+                "psr/cache-implementation": "Cache implementation to use with Minify::cache"
+            },
+            "time": "2021-01-06T15:18:10+00:00",
+            "bin": [
+                "bin/minifycss",
+                "bin/minifyjs"
+            ],
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "MatthiasMullie\\Minify\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Matthias Mullie",
+                    "email": "minify@mullie.eu",
+                    "homepage": "http://www.mullie.eu",
+                    "role": "Developer"
+                }
+            ],
+            "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
+            "homepage": "http://www.minifier.org",
+            "keywords": [
+                "JS",
+                "css",
+                "javascript",
+                "minifier",
+                "minify"
+            ],
+            "support": {
+                "issues": "https://github.com/matthiasmullie/minify/issues",
+                "source": "https://github.com/matthiasmullie/minify/tree/1.3.66"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/[user1",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/user2",
+                    "type": "github"
+                }
+            ],
+            "install-path": "../matthiasmullie/minify"
+        },
+        {
+            "name": "matthiasmullie/path-converter",
+            "version": "1.1.3",
+            "version_normalized": "1.1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/matthiasmullie/path-converter.git",
+                "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9",
+                "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8"
+            },
+            "time": "2019-02-05T23:41:09+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "MatthiasMullie\\PathConverter\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Matthias Mullie",
+                    "email": "pathconverter@mullie.eu",
+                    "homepage": "http://www.mullie.eu",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Relative path converter",
+            "homepage": "http://github.com/matthiasmullie/path-converter",
+            "keywords": [
+                "converter",
+                "path",
+                "paths",
+                "relative"
+            ],
+            "support": {
+                "issues": "https://github.com/matthiasmullie/path-converter/issues",
+                "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3"
+            },
+            "install-path": "../matthiasmullie/path-converter"
+        },
+        {
+            "name": "symfony/css-selector",
+            "version": "v5.2.4",
+            "version_normalized": "5.2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/css-selector.git",
+                "reference": "f65f217b3314504a1ec99c2d6ef69016bb13490f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/f65f217b3314504a1ec99c2d6ef69016bb13490f",
+                "reference": "f65f217b3314504a1ec99c2d6ef69016bb13490f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5"
+            },
+            "time": "2021-01-27T10:01:46+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\CssSelector\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Jean-François Simon",
+                    "email": "jeanfrancois.simon@sensiolabs.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Converts CSS selectors to XPath expressions",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/css-selector/tree/v5.2.4"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/css-selector"
+        },
+        {
+            "name": "voku/html-min",
+            "version": "4.4.8",
+            "version_normalized": "4.4.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/HtmlMin.git",
+                "reference": "4f700584abd70b308b7d06b8e4cfcc31711faaf9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/HtmlMin/zipball/4f700584abd70b308b7d06b8e4cfcc31711faaf9",
+                "reference": "4f700584abd70b308b7d06b8e4cfcc31711faaf9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "php": ">=7.0.0",
+                "voku/simple_html_dom": "~4.7.23"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6.0 || ~7.0"
+            },
+            "time": "2020-08-11T22:13:23+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "voku\\": "src/voku/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "http://www.moelleken.org/"
+                }
+            ],
+            "description": "HTML Compressor and Minifier",
+            "homepage": "https://github.com/voku/HtmlMin",
+            "keywords": [
+                "compress",
+                "compression",
+                "compressor",
+                "html",
+                "minifier"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/HtmlMin/issues",
+                "source": "https://github.com/voku/HtmlMin/tree/master"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                }
+            ],
+            "install-path": "../voku/html-min"
+        },
+        {
+            "name": "voku/simple_html_dom",
+            "version": "4.7.29",
+            "version_normalized": "4.7.29.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/simple_html_dom.git",
+                "reference": "079067c704b714b7c2813971297bb340307813e7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/simple_html_dom/zipball/079067c704b714b7c2813971297bb340307813e7",
+                "reference": "079067c704b714b7c2813971297bb340307813e7",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "ext-simplexml": "*",
+                "php": ">=7.0.0",
+                "symfony/css-selector": "~3.0 || ~4.0 || ~5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+            },
+            "suggest": {
+                "voku/portable-utf8": "If you need e.g. UTF-8 fixed output."
+            },
+            "time": "2021-03-29T14:56:56+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "voku\\helper\\": "src/voku/helper/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "dimabdc",
+                    "email": "support@titor.ru",
+                    "homepage": "http://github.com/dimabdc",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "http://www.moelleken.org/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Simple HTML DOM package.",
+            "homepage": "http://simplehtmldom.sourceforge.net/",
+            "keywords": [
+                "HTML Parser",
+                "dom",
+                "php dom"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/simple_html_dom/issues",
+                "source": "https://github.com/voku/simple_html_dom/tree/4.7.29"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/voku/simple_html_dom",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../voku/simple_html_dom"
+        }
+    ],
+    "dev": true,
+    "dev-package-names": []
+}

+ 69 - 0
packer/deps/vendor/composer/installed.php

@@ -0,0 +1,69 @@
+<?php return array (
+  'root' => 
+  array (
+    'pretty_version' => 'dev-master',
+    'version' => 'dev-master',
+    'aliases' => 
+    array (
+    ),
+    'reference' => 'ea843e061f44cf14125fc3280f232b55f34e1573',
+    'name' => '__root__',
+  ),
+  'versions' => 
+  array (
+    '__root__' => 
+    array (
+      'pretty_version' => 'dev-master',
+      'version' => 'dev-master',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'ea843e061f44cf14125fc3280f232b55f34e1573',
+    ),
+    'matthiasmullie/minify' => 
+    array (
+      'pretty_version' => '1.3.66',
+      'version' => '1.3.66.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
+    ),
+    'matthiasmullie/path-converter' => 
+    array (
+      'pretty_version' => '1.1.3',
+      'version' => '1.1.3.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
+    ),
+    'symfony/css-selector' => 
+    array (
+      'pretty_version' => 'v5.2.4',
+      'version' => '5.2.4.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'f65f217b3314504a1ec99c2d6ef69016bb13490f',
+    ),
+    'voku/html-min' => 
+    array (
+      'pretty_version' => '4.4.8',
+      'version' => '4.4.8.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '4f700584abd70b308b7d06b8e4cfcc31711faaf9',
+    ),
+    'voku/simple_html_dom' => 
+    array (
+      'pretty_version' => '4.7.29',
+      'version' => '4.7.29.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '079067c704b714b7c2813971297bb340307813e7',
+    ),
+  ),
+);

+ 26 - 0
packer/deps/vendor/composer/platform_check.php

@@ -0,0 +1,26 @@
+<?php
+
+// platform_check.php @generated by Composer
+
+$issues = array();
+
+if (!(PHP_VERSION_ID >= 70205)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.5". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+    if (!headers_sent()) {
+        header('HTTP/1.1 500 Internal Server Error');
+    }
+    if (!ini_get('display_errors')) {
+        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+            fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+        } elseif (!headers_sent()) {
+            echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+        }
+    }
+    trigger_error(
+        'Composer detected issues in your platform: ' . implode(' ', $issues),
+        E_USER_ERROR
+    );
+}

+ 12 - 0
packer/deps/vendor/matthiasmullie/minify/.github/FUNDING.yml

@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: [matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

+ 59 - 0
packer/deps/vendor/matthiasmullie/minify/CONTRIBUTING.md

@@ -0,0 +1,59 @@
+# How to contribute
+
+
+## Issues
+
+When [filing bugs](https://github.com/matthiasmullie/minify/issues/new),
+try to be as thorough as possible:
+* What version did you use?
+* What did you try to do? ***Please post the relevant parts of your code.***
+* What went wrong? ***Please include error messages, if any.***
+* What was the expected result?
+
+
+## Pull requests
+
+Bug fixes and general improvements to the existing codebase are always welcome.
+New features are also welcome, but will be judged on an individual basis. If
+you'd rather not risk wasting your time implementing a new feature only to see
+it turned down, please start the discussion by
+[opening an issue](https://github.com/matthiasmullie/minify/issues/new).
+
+Don't forget to add your changes to the [changelog](CHANGELOG.md).
+
+
+### Testing
+
+Please include tests for every change or addition to the code.
+To run the complete test suite:
+
+```sh
+vendor/bin/phpunit
+```
+
+When submitting a new pull request, please make sure that that the test suite
+passes (Travis CI will run it & report back on your pull request.)
+
+To run the tests on Windows, run `tests/convert_symlinks_to_windows_style.sh`
+from the command line in order to convert Linux-style test symlinks to
+Windows-style.
+
+
+### Coding standards
+
+All code must follow [PSR-2](http://www.php-fig.org/psr/psr-2/). Just make sure
+to run php-cs-fixer before submitting the code, it'll take care of the
+formatting for you:
+
+```sh
+vendor/bin/php-cs-fixer fix src
+vendor/bin/php-cs-fixer fix tests
+```
+
+Document the code thoroughly!
+
+
+## License
+
+Note that minify is MIT-licensed, which basically allows anyone to do
+anything they like with it, without restriction.

+ 13 - 0
packer/deps/vendor/matthiasmullie/minify/Dockerfile

@@ -0,0 +1,13 @@
+ARG version=cli
+FROM php:$version
+
+COPY . /var/www
+WORKDIR /var/www
+
+RUN apt-get update
+RUN apt-get install -y zip unzip libzip-dev git
+RUN docker-php-ext-install zip
+RUN docker-php-ext-install pcntl
+RUN curl -sS https://getcomposer.org/installer | php
+RUN mv composer.phar /usr/local/bin/composer
+RUN composer install

+ 18 - 0
packer/deps/vendor/matthiasmullie/minify/LICENSE

@@ -0,0 +1,18 @@
+Copyright (c) 2012 Matthias Mullie
+
+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.

+ 45 - 0
packer/deps/vendor/matthiasmullie/minify/bin/minifycss

@@ -0,0 +1,45 @@
+#!/usr/bin/env php
+<?php
+use MatthiasMullie\Minify;
+
+// command line utility to minify CSS
+if (file_exists(__DIR__ . '/../../../autoload.php')) {
+    // if composer install
+    require_once __DIR__ . '/../../../autoload.php';
+} else {
+    require_once __DIR__ . '/../src/Minify.php';
+    require_once __DIR__ . '/../src/CSS.php';
+    require_once __DIR__ . '/../src/Exception.php';
+}
+
+error_reporting(E_ALL);
+// check PHP setup for cli arguments
+if (!isset($_SERVER['argv']) && !isset($argv)) {
+    fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
+    exit(1);
+} elseif (!isset($argv)) {
+    $argv = $_SERVER['argv'];
+}
+// check if path to file given
+if (!isset($argv[1])) {
+    fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
+    exit(1);
+}
+// check if script run in cli environment
+if ('cli' !== php_sapi_name()) {
+    fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
+    exit(1);
+}
+// check if source file exists
+if (!file_exists($argv[1])) {
+    fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
+    exit(1);
+}
+
+try {
+    $minifier = new Minify\CSS($argv[1]);
+    echo $minifier->minify();
+} catch (Exception $e) {
+    fwrite(STDERR, $e->getMessage(), PHP_EOL);
+    exit(1);
+}

+ 45 - 0
packer/deps/vendor/matthiasmullie/minify/bin/minifyjs

@@ -0,0 +1,45 @@
+#!/usr/bin/env php
+<?php
+use MatthiasMullie\Minify;
+
+// command line utility to minify JS
+if (file_exists(__DIR__ . '/../../../autoload.php')) {
+    // if composer install
+    require_once __DIR__ . '/../../../autoload.php';
+} else {
+    require_once __DIR__ . '/../src/Minify.php';
+    require_once __DIR__ . '/../src/JS.php';
+    require_once __DIR__ . '/../src/Exception.php';
+}
+
+error_reporting(E_ALL);
+// check PHP setup for cli arguments
+if (!isset($_SERVER['argv']) && !isset($argv)) {
+    fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
+    exit(1);
+} elseif (!isset($argv)) {
+    $argv = $_SERVER['argv'];
+}
+// check if path to file given
+if (!isset($argv[1])) {
+    fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
+    exit(1);
+}
+// check if script run in cli environment
+if ('cli' !== php_sapi_name()) {
+    fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
+    exit(1);
+}
+// check if source file exists
+if (!file_exists($argv[1])) {
+    fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
+    exit(1);
+}
+
+try {
+    $minifier = new Minify\JS($argv[1]);
+    echo $minifier->minify();
+} catch (Exception $e) {
+    fwrite(STDERR, $e->getMessage(), PHP_EOL);
+    exit(1);
+}

+ 38 - 0
packer/deps/vendor/matthiasmullie/minify/composer.json

@@ -0,0 +1,38 @@
+{
+    "name": "matthiasmullie/minify",
+    "type": "library",
+    "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
+    "keywords": ["minify", "minifier", "css", "js", "javascript"],
+    "homepage": "http://www.minifier.org",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Matthias Mullie",
+            "homepage": "http://www.mullie.eu",
+            "email": "minify@mullie.eu",
+            "role": "Developer"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0",
+        "ext-pcre": "*",
+        "matthiasmullie/path-converter": "~1.1"
+    },
+    "require-dev": {
+        "matthiasmullie/scrapbook": "dev-master",
+        "phpunit/phpunit": ">=4.8",
+        "friendsofphp/php-cs-fixer": "~2.0"
+    },
+    "suggest": {
+        "psr/cache-implementation": "Cache implementation to use with Minify::cache"
+    },
+    "autoload": {
+        "psr-4": {
+            "MatthiasMullie\\Minify\\": "src/"
+        }
+    },
+    "bin": [
+        "bin/minifycss",
+        "bin/minifyjs"
+    ]
+}

+ 7 - 0
packer/deps/vendor/matthiasmullie/minify/data/js/keywords_after.txt

@@ -0,0 +1,7 @@
+in
+public
+extends
+private
+protected
+implements
+instanceof

+ 26 - 0
packer/deps/vendor/matthiasmullie/minify/data/js/keywords_before.txt

@@ -0,0 +1,26 @@
+do
+in
+let
+new
+var
+case
+else
+enum
+void
+with
+class
+const
+yield
+delete
+export
+import
+public
+static
+typeof
+extends
+package
+private
+function
+protected
+implements
+instanceof

+ 63 - 0
packer/deps/vendor/matthiasmullie/minify/data/js/keywords_reserved.txt

@@ -0,0 +1,63 @@
+do
+if
+in
+for
+let
+new
+try
+var
+case
+else
+enum
+eval
+null
+this
+true
+void
+with
+break
+catch
+class
+const
+false
+super
+throw
+while
+yield
+delete
+export
+import
+public
+return
+static
+switch
+typeof
+default
+extends
+finally
+package
+private
+continue
+debugger
+function
+arguments
+interface
+protected
+implements
+instanceof
+abstract
+boolean
+byte
+char
+double
+final
+float
+goto
+int
+long
+native
+short
+synchronized
+throws
+transient
+volatile

+ 46 - 0
packer/deps/vendor/matthiasmullie/minify/data/js/operators.txt

@@ -0,0 +1,46 @@
++
+-
+*
+/
+%
+=
++=
+-=
+*=
+/=
+%=
+<<=
+>>=
+>>>=
+&=
+^=
+|=
+&
+|
+^
+~
+<<
+>>
+>>>
+==
+===
+!=
+!==
+>
+<
+>=
+<=
+&&
+||
+!
+.
+[
+]
+?
+:
+,
+;
+(
+)
+{
+}

+ 43 - 0
packer/deps/vendor/matthiasmullie/minify/data/js/operators_after.txt

@@ -0,0 +1,43 @@
++
+-
+*
+/
+%
+=
++=
+-=
+*=
+/=
+%=
+<<=
+>>=
+>>>=
+&=
+^=
+|=
+&
+|
+^
+<<
+>>
+>>>
+==
+===
+!=
+!==
+>
+<
+>=
+<=
+&&
+||
+.
+[
+]
+?
+:
+,
+;
+(
+)
+}

+ 43 - 0
packer/deps/vendor/matthiasmullie/minify/data/js/operators_before.txt

@@ -0,0 +1,43 @@
++
+-
+*
+/
+%
+=
++=
+-=
+*=
+/=
+%=
+<<=
+>>=
+>>>=
+&=
+^=
+|=
+&
+|
+^
+~
+<<
+>>
+>>>
+==
+===
+!=
+!==
+>
+<
+>=
+<=
+&&
+||
+!
+.
+[
+?
+:
+,
+;
+(
+{

+ 46 - 0
packer/deps/vendor/matthiasmullie/minify/docker-compose.yml

@@ -0,0 +1,46 @@
+version: '2.1'
+services:
+  php:
+    build:
+      context: .
+      dockerfile: Dockerfile
+    volumes:
+      - ./src:/var/www/src
+      - ./data:/var/www/data
+      - ./tests:/var/www/tests
+      - ./phpunit.xml.dist:/var/www/phpunit.xml.dist
+  '8.0':
+    extends: php
+    build:
+      args:
+        version: 8.0-cli
+  '7.4':
+    extends: php
+    build:
+      args:
+        version: 7.4-cli
+  '7.3':
+    extends: php
+    build:
+      args:
+        version: 7.3-cli
+  '7.2':
+    extends: php
+    build:
+      args:
+        version: 7.2-cli
+  '7.1':
+    extends: php
+    build:
+      args:
+        version: 7.1-cli
+  '7.0':
+    extends: php
+    build:
+      args:
+        version: 7.0-cli
+  '5.6':
+    extends: php
+    build:
+      args:
+        version: 5.6-cli

+ 786 - 0
packer/deps/vendor/matthiasmullie/minify/src/CSS.php

@@ -0,0 +1,786 @@
+<?php
+/**
+ * CSS Minifier
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+
+namespace MatthiasMullie\Minify;
+
+use MatthiasMullie\Minify\Exceptions\FileImportException;
+use MatthiasMullie\PathConverter\ConverterInterface;
+use MatthiasMullie\PathConverter\Converter;
+
+/**
+ * CSS minifier
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @package Minify
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @author Tijs Verkoyen <minify@verkoyen.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class CSS extends Minify
+{
+    /**
+     * @var int maximum inport size in kB
+     */
+    protected $maxImportSize = 5;
+
+    /**
+     * @var string[] valid import extensions
+     */
+    protected $importExtensions = array(
+        'gif' => 'data:image/gif',
+        'png' => 'data:image/png',
+        'jpe' => 'data:image/jpeg',
+        'jpg' => 'data:image/jpeg',
+        'jpeg' => 'data:image/jpeg',
+        'svg' => 'data:image/svg+xml',
+        'woff' => 'data:application/x-font-woff',
+        'tif' => 'image/tiff',
+        'tiff' => 'image/tiff',
+        'xbm' => 'image/x-xbitmap',
+    );
+
+    /**
+     * Set the maximum size if files to be imported.
+     *
+     * Files larger than this size (in kB) will not be imported into the CSS.
+     * Importing files into the CSS as data-uri will save you some connections,
+     * but we should only import relatively small decorative images so that our
+     * CSS file doesn't get too bulky.
+     *
+     * @param int $size Size in kB
+     */
+    public function setMaxImportSize($size)
+    {
+        $this->maxImportSize = $size;
+    }
+
+    /**
+     * Set the type of extensions to be imported into the CSS (to save network
+     * connections).
+     * Keys of the array should be the file extensions & respective values
+     * should be the data type.
+     *
+     * @param string[] $extensions Array of file extensions
+     */
+    public function setImportExtensions(array $extensions)
+    {
+        $this->importExtensions = $extensions;
+    }
+
+    /**
+     * Move any import statements to the top.
+     *
+     * @param string $content Nearly finished CSS content
+     *
+     * @return string
+     */
+    protected function moveImportsToTop($content)
+    {
+        if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) {
+            // remove from content
+            foreach ($matches[0] as $import) {
+                $content = str_replace($import, '', $content);
+            }
+
+            // add to top
+            $content = implode(';', $matches[2]).';'.trim($content, ';');
+        }
+
+        return $content;
+    }
+
+    /**
+     * Combine CSS from import statements.
+     *
+     * @import's will be loaded and their content merged into the original file,
+     * to save HTTP requests.
+     *
+     * @param string   $source  The file to combine imports for
+     * @param string   $content The CSS content to combine imports for
+     * @param string[] $parents Parent paths, for circular reference checks
+     *
+     * @return string
+     *
+     * @throws FileImportException
+     */
+    protected function combineImports($source, $content, $parents)
+    {
+        $importRegexes = array(
+            // @import url(xxx)
+            '/
+            # import statement
+            @import
+
+            # whitespace
+            \s+
+
+                # open url()
+                url\(
+
+                    # (optional) open path enclosure
+                    (?P<quotes>["\']?)
+
+                        # fetch path
+                        (?P<path>.+?)
+
+                    # (optional) close path enclosure
+                    (?P=quotes)
+
+                # close url()
+                \)
+
+                # (optional) trailing whitespace
+                \s*
+
+                # (optional) media statement(s)
+                (?P<media>[^;]*)
+
+                # (optional) trailing whitespace
+                \s*
+
+            # (optional) closing semi-colon
+            ;?
+
+            /ix',
+
+            // @import 'xxx'
+            '/
+
+            # import statement
+            @import
+
+            # whitespace
+            \s+
+
+                # open path enclosure
+                (?P<quotes>["\'])
+
+                    # fetch path
+                    (?P<path>.+?)
+
+                # close path enclosure
+                (?P=quotes)
+
+                # (optional) trailing whitespace
+                \s*
+
+                # (optional) media statement(s)
+                (?P<media>[^;]*)
+
+                # (optional) trailing whitespace
+                \s*
+
+            # (optional) closing semi-colon
+            ;?
+
+            /ix',
+        );
+
+        // find all relative imports in css
+        $matches = array();
+        foreach ($importRegexes as $importRegex) {
+            if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
+                $matches = array_merge($matches, $regexMatches);
+            }
+        }
+
+        $search = array();
+        $replace = array();
+
+        // loop the matches
+        foreach ($matches as $match) {
+            // get the path for the file that will be imported
+            $importPath = dirname($source).'/'.$match['path'];
+
+            // only replace the import with the content if we can grab the
+            // content of the file
+            if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
+                continue;
+            }
+
+            // check if current file was not imported previously in the same
+            // import chain.
+            if (in_array($importPath, $parents)) {
+                throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
+            }
+
+            // grab referenced file & minify it (which may include importing
+            // yet other @import statements recursively)
+            $minifier = new self($importPath);
+            $minifier->setMaxImportSize($this->maxImportSize);
+            $minifier->setImportExtensions($this->importExtensions);
+            $importContent = $minifier->execute($source, $parents);
+
+            // check if this is only valid for certain media
+            if (!empty($match['media'])) {
+                $importContent = '@media '.$match['media'].'{'.$importContent.'}';
+            }
+
+            // add to replacement array
+            $search[] = $match[0];
+            $replace[] = $importContent;
+        }
+
+        // replace the import statements
+        return str_replace($search, $replace, $content);
+    }
+
+    /**
+     * Import files into the CSS, base64-ized.
+     *
+     * @url(image.jpg) images will be loaded and their content merged into the
+     * original file, to save HTTP requests.
+     *
+     * @param string $source  The file to import files for
+     * @param string $content The CSS content to import files for
+     *
+     * @return string
+     */
+    protected function importFiles($source, $content)
+    {
+        $regex = '/url\((["\']?)(.+?)\\1\)/i';
+        if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
+            $search = array();
+            $replace = array();
+
+            // loop the matches
+            foreach ($matches as $match) {
+                $extension = substr(strrchr($match[2], '.'), 1);
+                if ($extension && !array_key_exists($extension, $this->importExtensions)) {
+                    continue;
+                }
+
+                // get the path for the file that will be imported
+                $path = $match[2];
+                $path = dirname($source).'/'.$path;
+
+                // only replace the import with the content if we're able to get
+                // the content of the file, and it's relatively small
+                if ($this->canImportFile($path) && $this->canImportBySize($path)) {
+                    // grab content && base64-ize
+                    $importContent = $this->load($path);
+                    $importContent = base64_encode($importContent);
+
+                    // build replacement
+                    $search[] = $match[0];
+                    $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
+                }
+            }
+
+            // replace the import statements
+            $content = str_replace($search, $replace, $content);
+        }
+
+        return $content;
+    }
+
+    /**
+     * Minify the data.
+     * Perform CSS optimizations.
+     *
+     * @param string[optional] $path    Path to write the data to
+     * @param string[]         $parents Parent paths, for circular reference checks
+     *
+     * @return string The minified data
+     */
+    public function execute($path = null, $parents = array())
+    {
+        $content = '';
+
+        // loop CSS data (raw data and files)
+        foreach ($this->data as $source => $css) {
+            /*
+             * Let's first take out strings & comments, since we can't just
+             * remove whitespace anywhere. If whitespace occurs inside a string,
+             * we should leave it alone. E.g.:
+             * p { content: "a   test" }
+             */
+            $this->extractStrings();
+            $this->stripComments();
+            $this->extractMath();
+            $this->extractCustomProperties();
+            $css = $this->replace($css);
+
+            $css = $this->stripWhitespace($css);
+            $css = $this->shortenColors($css);
+            $css = $this->shortenZeroes($css);
+            $css = $this->shortenFontWeights($css);
+            $css = $this->stripEmptyTags($css);
+
+            // restore the string we've extracted earlier
+            $css = $this->restoreExtractedData($css);
+
+            $source = is_int($source) ? '' : $source;
+            $parents = $source ? array_merge($parents, array($source)) : $parents;
+            $css = $this->combineImports($source, $css, $parents);
+            $css = $this->importFiles($source, $css);
+
+            /*
+             * If we'll save to a new path, we'll have to fix the relative paths
+             * to be relative no longer to the source file, but to the new path.
+             * If we don't write to a file, fall back to same path so no
+             * conversion happens (because we still want it to go through most
+             * of the move code, which also addresses url() & @import syntax...)
+             */
+            $converter = $this->getPathConverter($source, $path ?: $source);
+            $css = $this->move($converter, $css);
+
+            // combine css
+            $content .= $css;
+        }
+
+        $content = $this->moveImportsToTop($content);
+
+        return $content;
+    }
+
+    /**
+     * Moving a css file should update all relative urls.
+     * Relative references (e.g. ../images/image.gif) in a certain css file,
+     * will have to be updated when a file is being saved at another location
+     * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
+     *
+     * @param ConverterInterface $converter Relative path converter
+     * @param string             $content   The CSS content to update relative urls for
+     *
+     * @return string
+     */
+    protected function move(ConverterInterface $converter, $content)
+    {
+        /*
+         * Relative path references will usually be enclosed by url(). @import
+         * is an exception, where url() is not necessary around the path (but is
+         * allowed).
+         * This *could* be 1 regular expression, where both regular expressions
+         * in this array are on different sides of a |. But we're using named
+         * patterns in both regexes, the same name on both regexes. This is only
+         * possible with a (?J) modifier, but that only works after a fairly
+         * recent PCRE version. That's why I'm doing 2 separate regular
+         * expressions & combining the matches after executing of both.
+         */
+        $relativeRegexes = array(
+            // url(xxx)
+            '/
+            # open url()
+            url\(
+
+                \s*
+
+                # open path enclosure
+                (?P<quotes>["\'])?
+
+                    # fetch path
+                    (?P<path>.+?)
+
+                # close path enclosure
+                (?(quotes)(?P=quotes))
+
+                \s*
+
+            # close url()
+            \)
+
+            /ix',
+
+            // @import "xxx"
+            '/
+            # import statement
+            @import
+
+            # whitespace
+            \s+
+
+                # we don\'t have to check for @import url(), because the
+                # condition above will already catch these
+
+                # open path enclosure
+                (?P<quotes>["\'])
+
+                    # fetch path
+                    (?P<path>.+?)
+
+                # close path enclosure
+                (?P=quotes)
+
+            /ix',
+        );
+
+        // find all relative urls in css
+        $matches = array();
+        foreach ($relativeRegexes as $relativeRegex) {
+            if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
+                $matches = array_merge($matches, $regexMatches);
+            }
+        }
+
+        $search = array();
+        $replace = array();
+
+        // loop all urls
+        foreach ($matches as $match) {
+            // determine if it's a url() or an @import match
+            $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
+
+            $url = $match['path'];
+            if ($this->canImportByPath($url)) {
+                // attempting to interpret GET-params makes no sense, so let's discard them for awhile
+                $params = strrchr($url, '?');
+                $url = $params ? substr($url, 0, -strlen($params)) : $url;
+
+                // fix relative url
+                $url = $converter->convert($url);
+
+                // now that the path has been converted, re-apply GET-params
+                $url .= $params;
+            }
+
+            /*
+             * Urls with control characters above 0x7e should be quoted.
+             * According to Mozilla's parser, whitespace is only allowed at the
+             * end of unquoted urls.
+             * Urls with `)` (as could happen with data: uris) should also be
+             * quoted to avoid being confused for the url() closing parentheses.
+             * And urls with a # have also been reported to cause issues.
+             * Urls with quotes inside should also remain escaped.
+             *
+             * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
+             * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
+             * @see https://github.com/matthiasmullie/minify/issues/193
+             */
+            $url = trim($url);
+            if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
+                $url = $match['quotes'] . $url . $match['quotes'];
+            }
+
+            // build replacement
+            $search[] = $match[0];
+            if ($type === 'url') {
+                $replace[] = 'url('.$url.')';
+            } elseif ($type === 'import') {
+                $replace[] = '@import "'.$url.'"';
+            }
+        }
+
+        // replace urls
+        return str_replace($search, $replace, $content);
+    }
+
+    /**
+     * Shorthand hex color codes.
+     * #FF0000 -> #F00.
+     *
+     * @param string $content The CSS content to shorten the hex color codes for
+     *
+     * @return string
+     */
+    protected function shortenColors($content)
+    {
+        $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?:([0-9a-z])\\4)?(?=[; }])/i', '#$1$2$3$4', $content);
+
+        // remove alpha channel if it's pointless...
+        $content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content);
+        $content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content);
+
+        $colors = array(
+            // we can shorten some even more by replacing them with their color name
+            '#F0FFFF' => 'azure',
+            '#F5F5DC' => 'beige',
+            '#A52A2A' => 'brown',
+            '#FF7F50' => 'coral',
+            '#FFD700' => 'gold',
+            '#808080' => 'gray',
+            '#008000' => 'green',
+            '#4B0082' => 'indigo',
+            '#FFFFF0' => 'ivory',
+            '#F0E68C' => 'khaki',
+            '#FAF0E6' => 'linen',
+            '#800000' => 'maroon',
+            '#000080' => 'navy',
+            '#808000' => 'olive',
+            '#CD853F' => 'peru',
+            '#FFC0CB' => 'pink',
+            '#DDA0DD' => 'plum',
+            '#800080' => 'purple',
+            '#F00' => 'red',
+            '#FA8072' => 'salmon',
+            '#A0522D' => 'sienna',
+            '#C0C0C0' => 'silver',
+            '#FFFAFA' => 'snow',
+            '#D2B48C' => 'tan',
+            '#FF6347' => 'tomato',
+            '#EE82EE' => 'violet',
+            '#F5DEB3' => 'wheat',
+            // or the other way around
+            'WHITE' => '#fff',
+            'BLACK' => '#000',
+        );
+
+        return preg_replace_callback(
+            '/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i',
+            function ($match) use ($colors) {
+                return $colors[strtoupper($match[0])];
+            },
+            $content
+        );
+    }
+
+    /**
+     * Shorten CSS font weights.
+     *
+     * @param string $content The CSS content to shorten the font weights for
+     *
+     * @return string
+     */
+    protected function shortenFontWeights($content)
+    {
+        $weights = array(
+            'normal' => 400,
+            'bold' => 700,
+        );
+
+        $callback = function ($match) use ($weights) {
+            return $match[1].$weights[$match[2]];
+        };
+
+        return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
+    }
+
+    /**
+     * Shorthand 0 values to plain 0, instead of e.g. -0em.
+     *
+     * @param string $content The CSS content to shorten the zero values for
+     *
+     * @return string
+     */
+    protected function shortenZeroes($content)
+    {
+        // we don't want to strip units in `calc()` expressions:
+        // `5px - 0px` is valid, but `5px - 0` is not
+        // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
+        // `10 * 0` is invalid
+        // we've extracted calcs earlier, so we don't need to worry about this
+
+        // reusable bits of code throughout these regexes:
+        // before & after are used to make sure we don't match lose unintended
+        // 0-like values (e.g. in #000, or in http://url/1.0)
+        // units can be stripped from 0 values, or used to recognize non 0
+        // values (where wa may be able to strip a .0 suffix)
+        $before = '(?<=[:(, ])';
+        $after = '(?=[ ,);}])';
+        $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
+
+        // strip units after zeroes (0px -> 0)
+        // NOTE: it should be safe to remove all units for a 0 value, but in
+        // practice, Webkit (especially Safari) seems to stumble over at least
+        // 0%, potentially other units as well. Only stripping 'px' for now.
+        // @see https://github.com/matthiasmullie/minify/issues/60
+        $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
+
+        // strip 0-digits (.0 -> 0)
+        $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
+        // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
+        $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
+        // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
+        $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
+        // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
+        $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
+
+        // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
+        $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
+
+        // IE doesn't seem to understand a unitless flex-basis value (correct -
+        // it goes against the spec), so let's add it in again (make it `%`,
+        // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
+        // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
+        $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
+        $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
+
+        return $content;
+    }
+
+    /**
+     * Strip empty tags from source code.
+     *
+     * @param string $content
+     *
+     * @return string
+     */
+    protected function stripEmptyTags($content)
+    {
+        $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
+        $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
+
+        return $content;
+    }
+
+    /**
+     * Strip comments from source code.
+     */
+    protected function stripComments()
+    {
+        // PHP only supports $this inside anonymous functions since 5.4
+        $minifier = $this;
+        $callback = function ($match) use ($minifier) {
+            $count = count($minifier->extracted);
+            $placeholder = '/*'.$count.'*/';
+            $minifier->extracted[$placeholder] = $match[0];
+
+            return $placeholder;
+        };
+        $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
+
+        $this->registerPattern('/\/\*.*?\*\//s', '');
+    }
+
+    /**
+     * Strip whitespace.
+     *
+     * @param string $content The CSS content to strip the whitespace for
+     *
+     * @return string
+     */
+    protected function stripWhitespace($content)
+    {
+        // remove leading & trailing whitespace
+        $content = preg_replace('/^\s*/m', '', $content);
+        $content = preg_replace('/\s*$/m', '', $content);
+
+        // replace newlines with a single space
+        $content = preg_replace('/\s+/', ' ', $content);
+
+        // remove whitespace around meta characters
+        // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
+        $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
+        $content = preg_replace('/([\[(:>\+])\s+/', '$1', $content);
+        $content = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
+        $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
+
+        // whitespace around + and - can only be stripped inside some pseudo-
+        // classes, like `:nth-child(3+2n)`
+        // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
+        // selectors like `div.weird- p`
+        $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
+        $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
+
+        // remove semicolon/whitespace followed by closing bracket
+        $content = str_replace(';}', '}', $content);
+
+        return trim($content);
+    }
+
+    /**
+     * Replace all occurrences of functions that may contain math, where
+     * whitespace around operators needs to be preserved (e.g. calc, clamp)
+     */
+    protected function extractMath()
+    {
+        $functions = array('calc', 'clamp', 'min', 'max');
+        $pattern = '/\b('. implode('|', $functions) .')(\(.+?)(?=$|;|})/m';
+
+        // PHP only supports $this inside anonymous functions since 5.4
+        $minifier = $this;
+        $callback = function ($match) use ($minifier, $pattern, &$callback) {
+            $function = $match[1];
+            $length = strlen($match[2]);
+            $expr = '';
+            $opened = 0;
+
+            // the regular expression for extracting math has 1 significant problem:
+            // it can't determine the correct closing parenthesis...
+            // instead, it'll match a larger portion of code to where it's certain that
+            // the calc() musts have ended, and we'll figure out which is the correct
+            // closing parenthesis here, by counting how many have opened
+            for ($i = 0; $i < $length; $i++) {
+                $char = $match[2][$i];
+                $expr .= $char;
+                if ($char === '(') {
+                    $opened++;
+                } elseif ($char === ')' && --$opened === 0) {
+                    break;
+                }
+            }
+
+            // now that we've figured out where the calc() starts and ends, extract it
+            $count = count($minifier->extracted);
+            $placeholder = 'math('.$count.')';
+            $minifier->extracted[$placeholder] = $function.'('.trim(substr($expr, 1, -1)).')';
+
+            // and since we've captured more code than required, we may have some leftover
+            // calc() in here too - go recursive on the remaining but of code to go figure
+            // that out and extract what is needed
+            $rest = str_replace($function.$expr, '', $match[0]);
+            $rest = preg_replace_callback($pattern, $callback, $rest);
+
+            return $placeholder.$rest;
+        };
+
+        $this->registerPattern($pattern, $callback);
+    }
+
+    /**
+     * Replace custom properties, whose values may be used in scenarios where
+     * we wouldn't want them to be minified (e.g. inside calc)
+     */
+    protected function extractCustomProperties()
+    {
+        // PHP only supports $this inside anonymous functions since 5.4
+        $minifier = $this;
+        $this->registerPattern(
+            '/(?<=^|[;}])(--[^:;{}"\'\s]+)\s*:([^;{}]+)/m',
+            function ($match) use ($minifier) {
+                $placeholder = '--custom-'. count($minifier->extracted) . ':0';
+                $minifier->extracted[$placeholder] = $match[1] .':'. trim($match[2]);
+                return $placeholder;
+
+            }
+        );
+    }
+
+    /**
+     * Check if file is small enough to be imported.
+     *
+     * @param string $path The path to the file
+     *
+     * @return bool
+     */
+    protected function canImportBySize($path)
+    {
+        return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
+    }
+
+    /**
+     * Check if file a file can be imported, going by the path.
+     *
+     * @param string $path
+     *
+     * @return bool
+     */
+    protected function canImportByPath($path)
+    {
+        return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
+    }
+
+    /**
+     * Return a converter to update relative paths to be relative to the new
+     * destination.
+     *
+     * @param string $source
+     * @param string $target
+     *
+     * @return ConverterInterface
+     */
+    protected function getPathConverter($source, $target)
+    {
+        return new Converter($source, $target);
+    }
+}

+ 20 - 0
packer/deps/vendor/matthiasmullie/minify/src/Exception.php

@@ -0,0 +1,20 @@
+<?php
+/**
+ * Base Exception
+ *
+ * @deprecated Use Exceptions\BasicException instead
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ */
+namespace MatthiasMullie\Minify;
+
+/**
+ * Base Exception Class
+ * @deprecated Use Exceptions\BasicException instead
+ *
+ * @package Minify
+ * @author Matthias Mullie <minify@mullie.eu>
+ */
+abstract class Exception extends \Exception
+{
+}

+ 23 - 0
packer/deps/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php

@@ -0,0 +1,23 @@
+<?php
+/**
+ * Basic exception
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify\Exceptions;
+
+use MatthiasMullie\Minify\Exception;
+
+/**
+ * Basic Exception Class
+ *
+ * @package Minify\Exception
+ * @author Matthias Mullie <minify@mullie.eu>
+ */
+abstract class BasicException extends Exception
+{
+}

+ 21 - 0
packer/deps/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * File Import Exception
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify\Exceptions;
+
+/**
+ * File Import Exception Class
+ *
+ * @package Minify\Exception
+ * @author Matthias Mullie <minify@mullie.eu>
+ */
+class FileImportException extends BasicException
+{
+}

+ 21 - 0
packer/deps/vendor/matthiasmullie/minify/src/Exceptions/IOException.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * IO Exception
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify\Exceptions;
+
+/**
+ * IO Exception Class
+ *
+ * @package Minify\Exception
+ * @author Matthias Mullie <minify@mullie.eu>
+ */
+class IOException extends BasicException
+{
+}

File diff suppressed because it is too large
+ 33 - 0
packer/deps/vendor/matthiasmullie/minify/src/JS.php


+ 501 - 0
packer/deps/vendor/matthiasmullie/minify/src/Minify.php

@@ -0,0 +1,501 @@
+<?php
+/**
+ * Abstract minifier class
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify;
+
+use MatthiasMullie\Minify\Exceptions\IOException;
+use Psr\Cache\CacheItemInterface;
+
+/**
+ * Abstract minifier class.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @package Minify
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+abstract class Minify
+{
+    /**
+     * The data to be minified.
+     *
+     * @var string[]
+     */
+    protected $data = array();
+
+    /**
+     * Array of patterns to match.
+     *
+     * @var string[]
+     */
+    protected $patterns = array();
+
+    /**
+     * This array will hold content of strings and regular expressions that have
+     * been extracted from the JS source code, so we can reliably match "code",
+     * without having to worry about potential "code-like" characters inside.
+     *
+     * @var string[]
+     */
+    public $extracted = array();
+
+    /**
+     * Init the minify class - optionally, code may be passed along already.
+     */
+    public function __construct(/* $data = null, ... */)
+    {
+        // it's possible to add the source through the constructor as well ;)
+        if (func_num_args()) {
+            call_user_func_array(array($this, 'add'), func_get_args());
+        }
+    }
+
+    /**
+     * Add a file or straight-up code to be minified.
+     *
+     * @param string|string[] $data
+     *
+     * @return static
+     */
+    public function add($data /* $data = null, ... */)
+    {
+        // bogus "usage" of parameter $data: scrutinizer warns this variable is
+        // not used (we're using func_get_args instead to support overloading),
+        // but it still needs to be defined because it makes no sense to have
+        // this function without argument :)
+        $args = array($data) + func_get_args();
+
+        // this method can be overloaded
+        foreach ($args as $data) {
+            if (is_array($data)) {
+                call_user_func_array(array($this, 'add'), $data);
+                continue;
+            }
+
+            // redefine var
+            $data = (string) $data;
+
+            // load data
+            $value = $this->load($data);
+            $key = ($data != $value) ? $data : count($this->data);
+
+            // replace CR linefeeds etc.
+            // @see https://github.com/matthiasmullie/minify/pull/139
+            $value = str_replace(array("\r\n", "\r"), "\n", $value);
+
+            // store data
+            $this->data[$key] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a file to be minified.
+     *
+     * @param string|string[] $data
+     *
+     * @return static
+     *
+     * @throws IOException
+     */
+    public function addFile($data /* $data = null, ... */)
+    {
+        // bogus "usage" of parameter $data: scrutinizer warns this variable is
+        // not used (we're using func_get_args instead to support overloading),
+        // but it still needs to be defined because it makes no sense to have
+        // this function without argument :)
+        $args = array($data) + func_get_args();
+
+        // this method can be overloaded
+        foreach ($args as $path) {
+            if (is_array($path)) {
+                call_user_func_array(array($this, 'addFile'), $path);
+                continue;
+            }
+
+            // redefine var
+            $path = (string) $path;
+
+            // check if we can read the file
+            if (!$this->canImportFile($path)) {
+                throw new IOException('The file "'.$path.'" could not be opened for reading. Check if PHP has enough permissions.');
+            }
+
+            $this->add($path);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Minify the data & (optionally) saves it to a file.
+     *
+     * @param string[optional] $path Path to write the data to
+     *
+     * @return string The minified data
+     */
+    public function minify($path = null)
+    {
+        $content = $this->execute($path);
+
+        // save to path
+        if ($path !== null) {
+            $this->save($content, $path);
+        }
+
+        return $content;
+    }
+
+    /**
+     * Minify & gzip the data & (optionally) saves it to a file.
+     *
+     * @param string[optional] $path  Path to write the data to
+     * @param int[optional]    $level Compression level, from 0 to 9
+     *
+     * @return string The minified & gzipped data
+     */
+    public function gzip($path = null, $level = 9)
+    {
+        $content = $this->execute($path);
+        $content = gzencode($content, $level, FORCE_GZIP);
+
+        // save to path
+        if ($path !== null) {
+            $this->save($content, $path);
+        }
+
+        return $content;
+    }
+
+    /**
+     * Minify the data & write it to a CacheItemInterface object.
+     *
+     * @param CacheItemInterface $item Cache item to write the data to
+     *
+     * @return CacheItemInterface Cache item with the minifier data
+     */
+    public function cache(CacheItemInterface $item)
+    {
+        $content = $this->execute();
+        $item->set($content);
+
+        return $item;
+    }
+
+    /**
+     * Minify the data.
+     *
+     * @param string[optional] $path Path to write the data to
+     *
+     * @return string The minified data
+     */
+    abstract public function execute($path = null);
+
+    /**
+     * Load data.
+     *
+     * @param string $data Either a path to a file or the content itself
+     *
+     * @return string
+     */
+    protected function load($data)
+    {
+        // check if the data is a file
+        if ($this->canImportFile($data)) {
+            $data = file_get_contents($data);
+
+            // strip BOM, if any
+            if (substr($data, 0, 3) == "\xef\xbb\xbf") {
+                $data = substr($data, 3);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Save to file.
+     *
+     * @param string $content The minified data
+     * @param string $path    The path to save the minified data to
+     *
+     * @throws IOException
+     */
+    protected function save($content, $path)
+    {
+        $handler = $this->openFileForWriting($path);
+
+        $this->writeToFile($handler, $content);
+
+        @fclose($handler);
+    }
+
+    /**
+     * Register a pattern to execute against the source content.
+     *
+     * @param string          $pattern     PCRE pattern
+     * @param string|callable $replacement Replacement value for matched pattern
+     */
+    protected function registerPattern($pattern, $replacement = '')
+    {
+        // study the pattern, we'll execute it more than once
+        $pattern .= 'S';
+
+        $this->patterns[] = array($pattern, $replacement);
+    }
+
+    /**
+     * We can't "just" run some regular expressions against JavaScript: it's a
+     * complex language. E.g. having an occurrence of // xyz would be a comment,
+     * unless it's used within a string. Of you could have something that looks
+     * like a 'string', but inside a comment.
+     * The only way to accurately replace these pieces is to traverse the JS one
+     * character at a time and try to find whatever starts first.
+     *
+     * @param string $content The content to replace patterns in
+     *
+     * @return string The (manipulated) content
+     */
+    protected function replace($content)
+    {
+        $processed = '';
+        $positions = array_fill(0, count($this->patterns), -1);
+        $matches = array();
+
+        while ($content) {
+            // find first match for all patterns
+            foreach ($this->patterns as $i => $pattern) {
+                list($pattern, $replacement) = $pattern;
+
+                // we can safely ignore patterns for positions we've unset earlier,
+                // because we know these won't show up anymore
+                if (array_key_exists($i, $positions) == false) {
+                    continue;
+                }
+
+                // no need to re-run matches that are still in the part of the
+                // content that hasn't been processed
+                if ($positions[$i] >= 0) {
+                    continue;
+                }
+
+                $match = null;
+                if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
+                    $matches[$i] = $match;
+
+                    // we'll store the match position as well; that way, we
+                    // don't have to redo all preg_matches after changing only
+                    // the first (we'll still know where those others are)
+                    $positions[$i] = $match[0][1];
+                } else {
+                    // if the pattern couldn't be matched, there's no point in
+                    // executing it again in later runs on this same content;
+                    // ignore this one until we reach end of content
+                    unset($matches[$i], $positions[$i]);
+                }
+            }
+
+            // no more matches to find: everything's been processed, break out
+            if (!$matches) {
+                $processed .= $content;
+                break;
+            }
+
+            // see which of the patterns actually found the first thing (we'll
+            // only want to execute that one, since we're unsure if what the
+            // other found was not inside what the first found)
+            $discardLength = min($positions);
+            $firstPattern = array_search($discardLength, $positions);
+            $match = $matches[$firstPattern][0][0];
+
+            // execute the pattern that matches earliest in the content string
+            list($pattern, $replacement) = $this->patterns[$firstPattern];
+            $replacement = $this->replacePattern($pattern, $replacement, $content);
+
+            // figure out which part of the string was unmatched; that's the
+            // part we'll execute the patterns on again next
+            $content = (string) substr($content, $discardLength);
+            $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
+
+            // move the replaced part to $processed and prepare $content to
+            // again match batch of patterns against
+            $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
+            $content = $unmatched;
+
+            // first match has been replaced & that content is to be left alone,
+            // the next matches will start after this replacement, so we should
+            // fix their offsets
+            foreach ($positions as $i => $position) {
+                $positions[$i] -= $discardLength + strlen($match);
+            }
+        }
+
+        return $processed;
+    }
+
+    /**
+     * This is where a pattern is matched against $content and the matches
+     * are replaced by their respective value.
+     * This function will be called plenty of times, where $content will always
+     * move up 1 character.
+     *
+     * @param string          $pattern     Pattern to match
+     * @param string|callable $replacement Replacement value
+     * @param string          $content     Content to match pattern against
+     *
+     * @return string
+     */
+    protected function replacePattern($pattern, $replacement, $content)
+    {
+        if (is_callable($replacement)) {
+            return preg_replace_callback($pattern, $replacement, $content, 1, $count);
+        } else {
+            return preg_replace($pattern, $replacement, $content, 1, $count);
+        }
+    }
+
+    /**
+     * Strings are a pattern we need to match, in order to ignore potential
+     * code-like content inside them, but we just want all of the string
+     * content to remain untouched.
+     *
+     * This method will replace all string content with simple STRING#
+     * placeholder text, so we've rid all strings from characters that may be
+     * misinterpreted. Original string content will be saved in $this->extracted
+     * and after doing all other minifying, we can restore the original content
+     * via restoreStrings().
+     *
+     * @param string[optional] $chars
+     * @param string[optional] $placeholderPrefix
+     */
+    protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
+    {
+        // PHP only supports $this inside anonymous functions since 5.4
+        $minifier = $this;
+        $callback = function ($match) use ($minifier, $placeholderPrefix) {
+            // check the second index here, because the first always contains a quote
+            if ($match[2] === '') {
+                /*
+                 * Empty strings need no placeholder; they can't be confused for
+                 * anything else anyway.
+                 * But we still needed to match them, for the extraction routine
+                 * to skip over this particular string.
+                 */
+                return $match[0];
+            }
+
+            $count = count($minifier->extracted);
+            $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
+            $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
+
+            return $placeholder;
+        };
+
+        /*
+         * The \\ messiness explained:
+         * * Don't count ' or " as end-of-string if it's escaped (has backslash
+         * in front of it)
+         * * Unless... that backslash itself is escaped (another leading slash),
+         * in which case it's no longer escaping the ' or "
+         * * So there can be either no backslash, or an even number
+         * * multiply all of that times 4, to account for the escaping that has
+         * to be done to pass the backslash into the PHP string without it being
+         * considered as escape-char (times 2) and to get it in the regex,
+         * escaped (times 2)
+         */
+        $this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
+    }
+
+    /**
+     * This method will restore all extracted data (strings, regexes) that were
+     * replaced with placeholder text in extract*(). The original content was
+     * saved in $this->extracted.
+     *
+     * @param string $content
+     *
+     * @return string
+     */
+    protected function restoreExtractedData($content)
+    {
+        if (!$this->extracted) {
+            // nothing was extracted, nothing to restore
+            return $content;
+        }
+
+        $content = strtr($content, $this->extracted);
+
+        $this->extracted = array();
+
+        return $content;
+    }
+
+    /**
+     * Check if the path is a regular file and can be read.
+     *
+     * @param string $path
+     *
+     * @return bool
+     */
+    protected function canImportFile($path)
+    {
+        $parsed = parse_url($path);
+        if (
+            // file is elsewhere
+            isset($parsed['host']) ||
+            // file responds to queries (may change, or need to bypass cache)
+            isset($parsed['query'])
+        ) {
+            return false;
+        }
+
+        return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
+    }
+
+    /**
+     * Attempts to open file specified by $path for writing.
+     *
+     * @param string $path The path to the file
+     *
+     * @return resource Specifier for the target file
+     *
+     * @throws IOException
+     */
+    protected function openFileForWriting($path)
+    {
+        if ($path === '' || ($handler = @fopen($path, 'w')) === false) {
+            throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
+        }
+
+        return $handler;
+    }
+
+    /**
+     * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
+     *
+     * @param resource $handler The resource to write to
+     * @param string   $content The content to write
+     * @param string   $path    The path to the file (for exception printing only)
+     *
+     * @throws IOException
+     */
+    protected function writeToFile($handler, $content, $path = '')
+    {
+        if (
+            !is_resource($handler) ||
+            ($result = @fwrite($handler, $content)) === false ||
+            ($result < strlen($content))
+        ) {
+            throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
+        }
+    }
+}

+ 18 - 0
packer/deps/vendor/matthiasmullie/path-converter/LICENSE

@@ -0,0 +1,18 @@
+Copyright (c) 2015 Matthias Mullie
+
+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.

+ 28 - 0
packer/deps/vendor/matthiasmullie/path-converter/composer.json

@@ -0,0 +1,28 @@
+{
+    "name": "matthiasmullie/path-converter",
+    "type": "library",
+    "description": "Relative path converter",
+    "keywords": ["relative", "path", "converter", "paths"],
+    "homepage": "http://github.com/matthiasmullie/path-converter",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Matthias Mullie",
+            "homepage": "http://www.mullie.eu",
+            "email": "pathconverter@mullie.eu",
+            "role": "Developer"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0",
+        "ext-pcre": "*"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.8"
+    },
+    "autoload": {
+        "psr-4": {
+            "MatthiasMullie\\PathConverter\\": "src/"
+        }
+    }
+}

+ 204 - 0
packer/deps/vendor/matthiasmullie/path-converter/src/Converter.php

@@ -0,0 +1,204 @@
+<?php
+
+namespace MatthiasMullie\PathConverter;
+
+/**
+ * Convert paths relative from 1 file to another.
+ *
+ * E.g.
+ *     ../../images/icon.jpg relative to /css/imports/icons.css
+ * becomes
+ *     ../images/icon.jpg relative to /css/minified.css
+ *
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
+ *
+ * @author Matthias Mullie <pathconverter@mullie.eu>
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class Converter implements ConverterInterface
+{
+    /**
+     * @var string
+     */
+    protected $from;
+
+    /**
+     * @var string
+     */
+    protected $to;
+
+    /**
+     * @param string $from The original base path (directory, not file!)
+     * @param string $to   The new base path (directory, not file!)
+     * @param string $root Root directory (defaults to `getcwd`)
+     */
+    public function __construct($from, $to, $root = '')
+    {
+        $shared = $this->shared($from, $to);
+        if ($shared === '') {
+            // when both paths have nothing in common, one of them is probably
+            // absolute while the other is relative
+            $root = $root ?: getcwd();
+            $from = strpos($from, $root) === 0 ? $from : preg_replace('/\/+/', '/', $root.'/'.$from);
+            $to = strpos($to, $root) === 0 ? $to : preg_replace('/\/+/', '/', $root.'/'.$to);
+
+            // or traveling the tree via `..`
+            // attempt to resolve path, or assume it's fine if it doesn't exist
+            $from = @realpath($from) ?: $from;
+            $to = @realpath($to) ?: $to;
+        }
+
+        $from = $this->dirname($from);
+        $to = $this->dirname($to);
+
+        $from = $this->normalize($from);
+        $to = $this->normalize($to);
+
+        $this->from = $from;
+        $this->to = $to;
+    }
+
+    /**
+     * Normalize path.
+     *
+     * @param string $path
+     *
+     * @return string
+     */
+    protected function normalize($path)
+    {
+        // deal with different operating systems' directory structure
+        $path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
+
+        // remove leading current directory.
+        if (substr($path, 0, 2) === './') {
+            $path = substr($path, 2);
+        }
+
+        // remove references to current directory in the path.
+        $path = str_replace('/./', '/', $path);
+
+        /*
+         * Example:
+         *     /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
+         * to
+         *     /home/forkcms/frontend/core/layout/images/img.gif
+         */
+        do {
+            $path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
+        } while ($count);
+
+        return $path;
+    }
+
+    /**
+     * Figure out the shared path of 2 locations.
+     *
+     * Example:
+     *     /home/forkcms/frontend/core/layout/images/img.gif
+     * and
+     *     /home/forkcms/frontend/cache/minified_css
+     * share
+     *     /home/forkcms/frontend
+     *
+     * @param string $path1
+     * @param string $path2
+     *
+     * @return string
+     */
+    protected function shared($path1, $path2)
+    {
+        // $path could theoretically be empty (e.g. no path is given), in which
+        // case it shouldn't expand to array(''), which would compare to one's
+        // root /
+        $path1 = $path1 ? explode('/', $path1) : array();
+        $path2 = $path2 ? explode('/', $path2) : array();
+
+        $shared = array();
+
+        // compare paths & strip identical ancestors
+        foreach ($path1 as $i => $chunk) {
+            if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
+                $shared[] = $chunk;
+            } else {
+                break;
+            }
+        }
+
+        return implode('/', $shared);
+    }
+
+    /**
+     * Convert paths relative from 1 file to another.
+     *
+     * E.g.
+     *     ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
+     * should become:
+     *     ../../core/layout/images/img.gif relative to
+     *     /home/forkcms/frontend/cache/minified_css
+     *
+     * @param string $path The relative path that needs to be converted
+     *
+     * @return string The new relative path
+     */
+    public function convert($path)
+    {
+        // quit early if conversion makes no sense
+        if ($this->from === $this->to) {
+            return $path;
+        }
+
+        $path = $this->normalize($path);
+        // if we're not dealing with a relative path, just return absolute
+        if (strpos($path, '/') === 0) {
+            return $path;
+        }
+
+        // normalize paths
+        $path = $this->normalize($this->from.'/'.$path);
+
+        // strip shared ancestor paths
+        $shared = $this->shared($path, $this->to);
+        $path = mb_substr($path, mb_strlen($shared));
+        $to = mb_substr($this->to, mb_strlen($shared));
+
+        // add .. for every directory that needs to be traversed to new path
+        $to = str_repeat('../', count(array_filter(explode('/', $to))));
+
+        return $to.ltrim($path, '/');
+    }
+
+    /**
+     * Attempt to get the directory name from a path.
+     *
+     * @param string $path
+     *
+     * @return string
+     */
+    protected function dirname($path)
+    {
+        if (@is_file($path)) {
+            return dirname($path);
+        }
+
+        if (@is_dir($path)) {
+            return rtrim($path, '/');
+        }
+
+        // no known file/dir, start making assumptions
+
+        // ends in / = dir
+        if (mb_substr($path, -1) === '/') {
+            return rtrim($path, '/');
+        }
+
+        // has a dot in the name, likely a file
+        if (preg_match('/.*\..*$/', basename($path)) !== 0) {
+            return dirname($path);
+        }
+
+        // you're on your own here!
+        return $path;
+    }
+}

+ 24 - 0
packer/deps/vendor/matthiasmullie/path-converter/src/ConverterInterface.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace MatthiasMullie\PathConverter;
+
+/**
+ * Convert file paths.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
+ *
+ * @author Matthias Mullie <pathconverter@mullie.eu>
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+interface ConverterInterface
+{
+    /**
+     * Convert file paths.
+     *
+     * @param string $path The path to be converted
+     *
+     * @return string The new path
+     */
+    public function convert($path);
+}

+ 23 - 0
packer/deps/vendor/matthiasmullie/path-converter/src/NoConverter.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace MatthiasMullie\PathConverter;
+
+/**
+ * Don't convert paths.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
+ *
+ * @author Matthias Mullie <pathconverter@mullie.eu>
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class NoConverter implements ConverterInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function convert($path)
+    {
+        return $path;
+    }
+}

+ 18 - 0
packer/deps/vendor/symfony/css-selector/CHANGELOG.md

@@ -0,0 +1,18 @@
+CHANGELOG
+=========
+
+4.4.0
+-----
+
+ * Added support for `*:only-of-type`
+
+2.8.0
+-----
+
+ * Added the `CssSelectorConverter` class as a non-static API for the component.
+ * Deprecated the `CssSelector` static API of the component.
+
+2.1.0
+-----
+
+ * none

+ 69 - 0
packer/deps/vendor/symfony/css-selector/CssSelectorConverter.php

@@ -0,0 +1,69 @@
+<?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\CssSelector;
+
+use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser;
+use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser;
+use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser;
+use Symfony\Component\CssSelector\Parser\Shortcut\HashParser;
+use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension;
+use Symfony\Component\CssSelector\XPath\Translator;
+
+/**
+ * CssSelectorConverter is the main entry point of the component and can convert CSS
+ * selectors to XPath expressions.
+ *
+ * @author Christophe Coevoet <stof@notk.org>
+ */
+class CssSelectorConverter
+{
+    private $translator;
+    private $cache;
+
+    private static $xmlCache = [];
+    private static $htmlCache = [];
+
+    /**
+     * @param bool $html Whether HTML support should be enabled. Disable it for XML documents
+     */
+    public function __construct(bool $html = true)
+    {
+        $this->translator = new Translator();
+
+        if ($html) {
+            $this->translator->registerExtension(new HtmlExtension($this->translator));
+            $this->cache = &self::$htmlCache;
+        } else {
+            $this->cache = &self::$xmlCache;
+        }
+
+        $this->translator
+            ->registerParserShortcut(new EmptyStringParser())
+            ->registerParserShortcut(new ElementParser())
+            ->registerParserShortcut(new ClassParser())
+            ->registerParserShortcut(new HashParser())
+        ;
+    }
+
+    /**
+     * Translates a CSS expression to its XPath equivalent.
+     *
+     * Optionally, a prefix can be added to the resulting XPath
+     * expression with the $prefix parameter.
+     *
+     * @return string
+     */
+    public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::')
+    {
+        return $this->cache[$prefix][$cssExpr] ?? $this->cache[$prefix][$cssExpr] = $this->translator->cssToXPath($cssExpr, $prefix);
+    }
+}

+ 24 - 0
packer/deps/vendor/symfony/css-selector/Exception/ExceptionInterface.php

@@ -0,0 +1,24 @@
+<?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\CssSelector\Exception;
+
+/**
+ * Interface for exceptions.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+interface ExceptionInterface extends \Throwable
+{
+}

+ 24 - 0
packer/deps/vendor/symfony/css-selector/Exception/ExpressionErrorException.php

@@ -0,0 +1,24 @@
+<?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\CssSelector\Exception;
+
+/**
+ * ParseException is thrown when a CSS selector syntax is not valid.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+class ExpressionErrorException extends ParseException
+{
+}

+ 24 - 0
packer/deps/vendor/symfony/css-selector/Exception/InternalErrorException.php

@@ -0,0 +1,24 @@
+<?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\CssSelector\Exception;
+
+/**
+ * ParseException is thrown when a CSS selector syntax is not valid.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+class InternalErrorException extends ParseException
+{
+}

+ 24 - 0
packer/deps/vendor/symfony/css-selector/Exception/ParseException.php

@@ -0,0 +1,24 @@
+<?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\CssSelector\Exception;
+
+/**
+ * ParseException is thrown when a CSS selector syntax is not valid.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ParseException extends \Exception implements ExceptionInterface
+{
+}

+ 65 - 0
packer/deps/vendor/symfony/css-selector/Exception/SyntaxErrorException.php

@@ -0,0 +1,65 @@
+<?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\CssSelector\Exception;
+
+use Symfony\Component\CssSelector\Parser\Token;
+
+/**
+ * ParseException is thrown when a CSS selector syntax is not valid.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+class SyntaxErrorException extends ParseException
+{
+    /**
+     * @return self
+     */
+    public static function unexpectedToken(string $expectedValue, Token $foundToken)
+    {
+        return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken));
+    }
+
+    /**
+     * @return self
+     */
+    public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation)
+    {
+        return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation));
+    }
+
+    /**
+     * @return self
+     */
+    public static function unclosedString(int $position)
+    {
+        return new self(sprintf('Unclosed/invalid string at %s.', $position));
+    }
+
+    /**
+     * @return self
+     */
+    public static function nestedNot()
+    {
+        return new self('Got nested ::not().');
+    }
+
+    /**
+     * @return self
+     */
+    public static function stringAsFunctionArgument()
+    {
+        return new self('String not allowed as function argument.');
+    }
+}

+ 19 - 0
packer/deps/vendor/symfony/css-selector/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2004-2021 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.

+ 39 - 0
packer/deps/vendor/symfony/css-selector/Node/AbstractNode.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\CssSelector\Node;
+
+/**
+ * Abstract base node class.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+abstract class AbstractNode implements NodeInterface
+{
+    /**
+     * @var string
+     */
+    private $nodeName;
+
+    public function getNodeName(): string
+    {
+        if (null === $this->nodeName) {
+            $this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', static::class);
+        }
+
+        return $this->nodeName;
+    }
+}

+ 85 - 0
packer/deps/vendor/symfony/css-selector/Node/AttributeNode.php

@@ -0,0 +1,85 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a "<selector>[<namespace>|<attribute> <operator> <value>]" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class AttributeNode extends AbstractNode
+{
+    private $selector;
+    private $namespace;
+    private $attribute;
+    private $operator;
+    private $value;
+
+    public function __construct(NodeInterface $selector, ?string $namespace, string $attribute, string $operator, ?string $value)
+    {
+        $this->selector = $selector;
+        $this->namespace = $namespace;
+        $this->attribute = $attribute;
+        $this->operator = $operator;
+        $this->value = $value;
+    }
+
+    public function getSelector(): NodeInterface
+    {
+        return $this->selector;
+    }
+
+    public function getNamespace(): ?string
+    {
+        return $this->namespace;
+    }
+
+    public function getAttribute(): string
+    {
+        return $this->attribute;
+    }
+
+    public function getOperator(): string
+    {
+        return $this->operator;
+    }
+
+    public function getValue(): ?string
+    {
+        return $this->value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        $attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute;
+
+        return 'exists' === $this->operator
+            ? sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute)
+            : sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value);
+    }
+}

+ 60 - 0
packer/deps/vendor/symfony/css-selector/Node/ClassNode.php

@@ -0,0 +1,60 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a "<selector>.<name>" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class ClassNode extends AbstractNode
+{
+    private $selector;
+    private $name;
+
+    public function __construct(NodeInterface $selector, string $name)
+    {
+        $this->selector = $selector;
+        $this->name = $name;
+    }
+
+    public function getSelector(): NodeInterface
+    {
+        return $this->selector;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name);
+    }
+}

+ 69 - 0
packer/deps/vendor/symfony/css-selector/Node/CombinedSelectorNode.php

@@ -0,0 +1,69 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a combined node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class CombinedSelectorNode extends AbstractNode
+{
+    private $selector;
+    private $combinator;
+    private $subSelector;
+
+    public function __construct(NodeInterface $selector, string $combinator, NodeInterface $subSelector)
+    {
+        $this->selector = $selector;
+        $this->combinator = $combinator;
+        $this->subSelector = $subSelector;
+    }
+
+    public function getSelector(): NodeInterface
+    {
+        return $this->selector;
+    }
+
+    public function getCombinator(): string
+    {
+        return $this->combinator;
+    }
+
+    public function getSubSelector(): NodeInterface
+    {
+        return $this->subSelector;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        $combinator = ' ' === $this->combinator ? '<followed>' : $this->combinator;
+
+        return sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector);
+    }
+}

+ 62 - 0
packer/deps/vendor/symfony/css-selector/Node/ElementNode.php

@@ -0,0 +1,62 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a "<namespace>|<element>" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class ElementNode extends AbstractNode
+{
+    private $namespace;
+    private $element;
+
+    public function __construct(string $namespace = null, string $element = null)
+    {
+        $this->namespace = $namespace;
+        $this->element = $element;
+    }
+
+    public function getNamespace(): ?string
+    {
+        return $this->namespace;
+    }
+
+    public function getElement(): ?string
+    {
+        return $this->element;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return new Specificity(0, 0, $this->element ? 1 : 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        $element = $this->element ?: '*';
+
+        return sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element);
+    }
+}

+ 79 - 0
packer/deps/vendor/symfony/css-selector/Node/FunctionNode.php

@@ -0,0 +1,79 @@
+<?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\CssSelector\Node;
+
+use Symfony\Component\CssSelector\Parser\Token;
+
+/**
+ * Represents a "<selector>:<name>(<arguments>)" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class FunctionNode extends AbstractNode
+{
+    private $selector;
+    private $name;
+    private $arguments;
+
+    /**
+     * @param Token[] $arguments
+     */
+    public function __construct(NodeInterface $selector, string $name, array $arguments = [])
+    {
+        $this->selector = $selector;
+        $this->name = strtolower($name);
+        $this->arguments = $arguments;
+    }
+
+    public function getSelector(): NodeInterface
+    {
+        return $this->selector;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * @return Token[]
+     */
+    public function getArguments(): array
+    {
+        return $this->arguments;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        $arguments = implode(', ', array_map(function (Token $token) {
+            return "'".$token->getValue()."'";
+        }, $this->arguments));
+
+        return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : '');
+    }
+}

+ 60 - 0
packer/deps/vendor/symfony/css-selector/Node/HashNode.php

@@ -0,0 +1,60 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a "<selector>#<id>" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class HashNode extends AbstractNode
+{
+    private $selector;
+    private $id;
+
+    public function __construct(NodeInterface $selector, string $id)
+    {
+        $this->selector = $selector;
+        $this->id = $id;
+    }
+
+    public function getSelector(): NodeInterface
+    {
+        return $this->selector;
+    }
+
+    public function getId(): string
+    {
+        return $this->id;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id);
+    }
+}

+ 60 - 0
packer/deps/vendor/symfony/css-selector/Node/NegationNode.php

@@ -0,0 +1,60 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a "<selector>:not(<identifier>)" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class NegationNode extends AbstractNode
+{
+    private $selector;
+    private $subSelector;
+
+    public function __construct(NodeInterface $selector, NodeInterface $subSelector)
+    {
+        $this->selector = $selector;
+        $this->subSelector = $subSelector;
+    }
+
+    public function getSelector(): NodeInterface
+    {
+        return $this->selector;
+    }
+
+    public function getSubSelector(): NodeInterface
+    {
+        return $this->subSelector;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector);
+    }
+}

+ 31 - 0
packer/deps/vendor/symfony/css-selector/Node/NodeInterface.php

@@ -0,0 +1,31 @@
+<?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\CssSelector\Node;
+
+/**
+ * Interface for nodes.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+interface NodeInterface
+{
+    public function getNodeName(): string;
+
+    public function getSpecificity(): Specificity;
+
+    public function __toString(): string;
+}

+ 60 - 0
packer/deps/vendor/symfony/css-selector/Node/PseudoNode.php

@@ -0,0 +1,60 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a "<selector>:<identifier>" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class PseudoNode extends AbstractNode
+{
+    private $selector;
+    private $identifier;
+
+    public function __construct(NodeInterface $selector, string $identifier)
+    {
+        $this->selector = $selector;
+        $this->identifier = strtolower($identifier);
+    }
+
+    public function getSelector(): NodeInterface
+    {
+        return $this->selector;
+    }
+
+    public function getIdentifier(): string
+    {
+        return $this->identifier;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier);
+    }
+}

+ 60 - 0
packer/deps/vendor/symfony/css-selector/Node/SelectorNode.php

@@ -0,0 +1,60 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a "<selector>(::|:)<pseudoElement>" node.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class SelectorNode extends AbstractNode
+{
+    private $tree;
+    private $pseudoElement;
+
+    public function __construct(NodeInterface $tree, string $pseudoElement = null)
+    {
+        $this->tree = $tree;
+        $this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null;
+    }
+
+    public function getTree(): NodeInterface
+    {
+        return $this->tree;
+    }
+
+    public function getPseudoElement(): ?string
+    {
+        return $this->pseudoElement;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSpecificity(): Specificity
+    {
+        return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString(): string
+    {
+        return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : '');
+    }
+}

+ 73 - 0
packer/deps/vendor/symfony/css-selector/Node/Specificity.php

@@ -0,0 +1,73 @@
+<?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\CssSelector\Node;
+
+/**
+ * Represents a node specificity.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @see http://www.w3.org/TR/selectors/#specificity
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class Specificity
+{
+    public const A_FACTOR = 100;
+    public const B_FACTOR = 10;
+    public const C_FACTOR = 1;
+
+    private $a;
+    private $b;
+    private $c;
+
+    public function __construct(int $a, int $b, int $c)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+    }
+
+    public function plus(self $specificity): self
+    {
+        return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c);
+    }
+
+    public function getValue(): int
+    {
+        return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR;
+    }
+
+    /**
+     * Returns -1 if the object specificity is lower than the argument,
+     * 0 if they are equal, and 1 if the argument is lower.
+     */
+    public function compareTo(self $specificity): int
+    {
+        if ($this->a !== $specificity->a) {
+            return $this->a > $specificity->a ? 1 : -1;
+        }
+
+        if ($this->b !== $specificity->b) {
+            return $this->b > $specificity->b ? 1 : -1;
+        }
+
+        if ($this->c !== $specificity->c) {
+            return $this->c > $specificity->c ? 1 : -1;
+        }
+
+        return 0;
+    }
+}

+ 48 - 0
packer/deps/vendor/symfony/css-selector/Parser/Handler/CommentHandler.php

@@ -0,0 +1,48 @@
+<?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\CssSelector\Parser\Handler;
+
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector comment handler.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class CommentHandler implements HandlerInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function handle(Reader $reader, TokenStream $stream): bool
+    {
+        if ('/*' !== $reader->getSubstring(2)) {
+            return false;
+        }
+
+        $offset = $reader->getOffset('*/');
+
+        if (false === $offset) {
+            $reader->moveToEnd();
+        } else {
+            $reader->moveForward($offset + 2);
+        }
+
+        return true;
+    }
+}

+ 30 - 0
packer/deps/vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php

@@ -0,0 +1,30 @@
+<?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\CssSelector\Parser\Handler;
+
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector handler interface.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+interface HandlerInterface
+{
+    public function handle(Reader $reader, TokenStream $stream): bool;
+}

+ 58 - 0
packer/deps/vendor/symfony/css-selector/Parser/Handler/HashHandler.php

@@ -0,0 +1,58 @@
+<?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\CssSelector\Parser\Handler;
+
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\Token;
+use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
+use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector comment handler.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class HashHandler implements HandlerInterface
+{
+    private $patterns;
+    private $escaping;
+
+    public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
+    {
+        $this->patterns = $patterns;
+        $this->escaping = $escaping;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function handle(Reader $reader, TokenStream $stream): bool
+    {
+        $match = $reader->findPattern($this->patterns->getHashPattern());
+
+        if (!$match) {
+            return false;
+        }
+
+        $value = $this->escaping->escapeUnicode($match[1]);
+        $stream->push(new Token(Token::TYPE_HASH, $value, $reader->getPosition()));
+        $reader->moveForward(\strlen($match[0]));
+
+        return true;
+    }
+}

+ 58 - 0
packer/deps/vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php

@@ -0,0 +1,58 @@
+<?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\CssSelector\Parser\Handler;
+
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\Token;
+use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
+use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector comment handler.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class IdentifierHandler implements HandlerInterface
+{
+    private $patterns;
+    private $escaping;
+
+    public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
+    {
+        $this->patterns = $patterns;
+        $this->escaping = $escaping;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function handle(Reader $reader, TokenStream $stream): bool
+    {
+        $match = $reader->findPattern($this->patterns->getIdentifierPattern());
+
+        if (!$match) {
+            return false;
+        }
+
+        $value = $this->escaping->escapeUnicode($match[0]);
+        $stream->push(new Token(Token::TYPE_IDENTIFIER, $value, $reader->getPosition()));
+        $reader->moveForward(\strlen($match[0]));
+
+        return true;
+    }
+}

+ 54 - 0
packer/deps/vendor/symfony/css-selector/Parser/Handler/NumberHandler.php

@@ -0,0 +1,54 @@
+<?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\CssSelector\Parser\Handler;
+
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\Token;
+use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector comment handler.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class NumberHandler implements HandlerInterface
+{
+    private $patterns;
+
+    public function __construct(TokenizerPatterns $patterns)
+    {
+        $this->patterns = $patterns;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function handle(Reader $reader, TokenStream $stream): bool
+    {
+        $match = $reader->findPattern($this->patterns->getNumberPattern());
+
+        if (!$match) {
+            return false;
+        }
+
+        $stream->push(new Token(Token::TYPE_NUMBER, $match[0], $reader->getPosition()));
+        $reader->moveForward(\strlen($match[0]));
+
+        return true;
+    }
+}

+ 77 - 0
packer/deps/vendor/symfony/css-selector/Parser/Handler/StringHandler.php

@@ -0,0 +1,77 @@
+<?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\CssSelector\Parser\Handler;
+
+use Symfony\Component\CssSelector\Exception\InternalErrorException;
+use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\Token;
+use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
+use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector comment handler.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class StringHandler implements HandlerInterface
+{
+    private $patterns;
+    private $escaping;
+
+    public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
+    {
+        $this->patterns = $patterns;
+        $this->escaping = $escaping;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function handle(Reader $reader, TokenStream $stream): bool
+    {
+        $quote = $reader->getSubstring(1);
+
+        if (!\in_array($quote, ["'", '"'])) {
+            return false;
+        }
+
+        $reader->moveForward(1);
+        $match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote));
+
+        if (!$match) {
+            throw new InternalErrorException(sprintf('Should have found at least an empty match at %d.', $reader->getPosition()));
+        }
+
+        // check unclosed strings
+        if (\strlen($match[0]) === $reader->getRemainingLength()) {
+            throw SyntaxErrorException::unclosedString($reader->getPosition() - 1);
+        }
+
+        // check quotes pairs validity
+        if ($quote !== $reader->getSubstring(1, \strlen($match[0]))) {
+            throw SyntaxErrorException::unclosedString($reader->getPosition() - 1);
+        }
+
+        $string = $this->escaping->escapeUnicodeAndNewLine($match[0]);
+        $stream->push(new Token(Token::TYPE_STRING, $string, $reader->getPosition()));
+        $reader->moveForward(\strlen($match[0]) + 1);
+
+        return true;
+    }
+}

+ 46 - 0
packer/deps/vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.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\CssSelector\Parser\Handler;
+
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\Token;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector whitespace handler.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class WhitespaceHandler implements HandlerInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function handle(Reader $reader, TokenStream $stream): bool
+    {
+        $match = $reader->findPattern('~^[ \t\r\n\f]+~');
+
+        if (false === $match) {
+            return false;
+        }
+
+        $stream->push(new Token(Token::TYPE_WHITESPACE, $match[0], $reader->getPosition()));
+        $reader->moveForward(\strlen($match[0]));
+
+        return true;
+    }
+}

+ 353 - 0
packer/deps/vendor/symfony/css-selector/Parser/Parser.php

@@ -0,0 +1,353 @@
+<?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\CssSelector\Parser;
+
+use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
+use Symfony\Component\CssSelector\Node;
+use Symfony\Component\CssSelector\Parser\Tokenizer\Tokenizer;
+
+/**
+ * CSS selector parser.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class Parser implements ParserInterface
+{
+    private $tokenizer;
+
+    public function __construct(Tokenizer $tokenizer = null)
+    {
+        $this->tokenizer = $tokenizer ?: new Tokenizer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parse(string $source): array
+    {
+        $reader = new Reader($source);
+        $stream = $this->tokenizer->tokenize($reader);
+
+        return $this->parseSelectorList($stream);
+    }
+
+    /**
+     * Parses the arguments for ":nth-child()" and friends.
+     *
+     * @param Token[] $tokens
+     *
+     * @throws SyntaxErrorException
+     */
+    public static function parseSeries(array $tokens): array
+    {
+        foreach ($tokens as $token) {
+            if ($token->isString()) {
+                throw SyntaxErrorException::stringAsFunctionArgument();
+            }
+        }
+
+        $joined = trim(implode('', array_map(function (Token $token) {
+            return $token->getValue();
+        }, $tokens)));
+
+        $int = function ($string) {
+            if (!is_numeric($string)) {
+                throw SyntaxErrorException::stringAsFunctionArgument();
+            }
+
+            return (int) $string;
+        };
+
+        switch (true) {
+            case 'odd' === $joined:
+                return [2, 1];
+            case 'even' === $joined:
+                return [2, 0];
+            case 'n' === $joined:
+                return [1, 0];
+            case false === strpos($joined, 'n'):
+                return [0, $int($joined)];
+        }
+
+        $split = explode('n', $joined);
+        $first = $split[0] ?? null;
+
+        return [
+            $first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1,
+            isset($split[1]) && $split[1] ? $int($split[1]) : 0,
+        ];
+    }
+
+    private function parseSelectorList(TokenStream $stream): array
+    {
+        $stream->skipWhitespace();
+        $selectors = [];
+
+        while (true) {
+            $selectors[] = $this->parserSelectorNode($stream);
+
+            if ($stream->getPeek()->isDelimiter([','])) {
+                $stream->getNext();
+                $stream->skipWhitespace();
+            } else {
+                break;
+            }
+        }
+
+        return $selectors;
+    }
+
+    private function parserSelectorNode(TokenStream $stream): Node\SelectorNode
+    {
+        [$result, $pseudoElement] = $this->parseSimpleSelector($stream);
+
+        while (true) {
+            $stream->skipWhitespace();
+            $peek = $stream->getPeek();
+
+            if ($peek->isFileEnd() || $peek->isDelimiter([','])) {
+                break;
+            }
+
+            if (null !== $pseudoElement) {
+                throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
+            }
+
+            if ($peek->isDelimiter(['+', '>', '~'])) {
+                $combinator = $stream->getNext()->getValue();
+                $stream->skipWhitespace();
+            } else {
+                $combinator = ' ';
+            }
+
+            [$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream);
+            $result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector);
+        }
+
+        return new Node\SelectorNode($result, $pseudoElement);
+    }
+
+    /**
+     * Parses next simple node (hash, class, pseudo, negation).
+     *
+     * @throws SyntaxErrorException
+     */
+    private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = false): array
+    {
+        $stream->skipWhitespace();
+
+        $selectorStart = \count($stream->getUsed());
+        $result = $this->parseElementNode($stream);
+        $pseudoElement = null;
+
+        while (true) {
+            $peek = $stream->getPeek();
+            if ($peek->isWhitespace()
+                || $peek->isFileEnd()
+                || $peek->isDelimiter([',', '+', '>', '~'])
+                || ($insideNegation && $peek->isDelimiter([')']))
+            ) {
+                break;
+            }
+
+            if (null !== $pseudoElement) {
+                throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
+            }
+
+            if ($peek->isHash()) {
+                $result = new Node\HashNode($result, $stream->getNext()->getValue());
+            } elseif ($peek->isDelimiter(['.'])) {
+                $stream->getNext();
+                $result = new Node\ClassNode($result, $stream->getNextIdentifier());
+            } elseif ($peek->isDelimiter(['['])) {
+                $stream->getNext();
+                $result = $this->parseAttributeNode($result, $stream);
+            } elseif ($peek->isDelimiter([':'])) {
+                $stream->getNext();
+
+                if ($stream->getPeek()->isDelimiter([':'])) {
+                    $stream->getNext();
+                    $pseudoElement = $stream->getNextIdentifier();
+
+                    continue;
+                }
+
+                $identifier = $stream->getNextIdentifier();
+                if (\in_array(strtolower($identifier), ['first-line', 'first-letter', 'before', 'after'])) {
+                    // Special case: CSS 2.1 pseudo-elements can have a single ':'.
+                    // Any new pseudo-element must have two.
+                    $pseudoElement = $identifier;
+
+                    continue;
+                }
+
+                if (!$stream->getPeek()->isDelimiter(['('])) {
+                    $result = new Node\PseudoNode($result, $identifier);
+
+                    continue;
+                }
+
+                $stream->getNext();
+                $stream->skipWhitespace();
+
+                if ('not' === strtolower($identifier)) {
+                    if ($insideNegation) {
+                        throw SyntaxErrorException::nestedNot();
+                    }
+
+                    [$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true);
+                    $next = $stream->getNext();
+
+                    if (null !== $argumentPseudoElement) {
+                        throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()');
+                    }
+
+                    if (!$next->isDelimiter([')'])) {
+                        throw SyntaxErrorException::unexpectedToken('")"', $next);
+                    }
+
+                    $result = new Node\NegationNode($result, $argument);
+                } else {
+                    $arguments = [];
+                    $next = null;
+
+                    while (true) {
+                        $stream->skipWhitespace();
+                        $next = $stream->getNext();
+
+                        if ($next->isIdentifier()
+                            || $next->isString()
+                            || $next->isNumber()
+                            || $next->isDelimiter(['+', '-'])
+                        ) {
+                            $arguments[] = $next;
+                        } elseif ($next->isDelimiter([')'])) {
+                            break;
+                        } else {
+                            throw SyntaxErrorException::unexpectedToken('an argument', $next);
+                        }
+                    }
+
+                    if (empty($arguments)) {
+                        throw SyntaxErrorException::unexpectedToken('at least one argument', $next);
+                    }
+
+                    $result = new Node\FunctionNode($result, $identifier, $arguments);
+                }
+            } else {
+                throw SyntaxErrorException::unexpectedToken('selector', $peek);
+            }
+        }
+
+        if (\count($stream->getUsed()) === $selectorStart) {
+            throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek());
+        }
+
+        return [$result, $pseudoElement];
+    }
+
+    private function parseElementNode(TokenStream $stream): Node\ElementNode
+    {
+        $peek = $stream->getPeek();
+
+        if ($peek->isIdentifier() || $peek->isDelimiter(['*'])) {
+            if ($peek->isIdentifier()) {
+                $namespace = $stream->getNext()->getValue();
+            } else {
+                $stream->getNext();
+                $namespace = null;
+            }
+
+            if ($stream->getPeek()->isDelimiter(['|'])) {
+                $stream->getNext();
+                $element = $stream->getNextIdentifierOrStar();
+            } else {
+                $element = $namespace;
+                $namespace = null;
+            }
+        } else {
+            $element = $namespace = null;
+        }
+
+        return new Node\ElementNode($namespace, $element);
+    }
+
+    private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream): Node\AttributeNode
+    {
+        $stream->skipWhitespace();
+        $attribute = $stream->getNextIdentifierOrStar();
+
+        if (null === $attribute && !$stream->getPeek()->isDelimiter(['|'])) {
+            throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek());
+        }
+
+        if ($stream->getPeek()->isDelimiter(['|'])) {
+            $stream->getNext();
+
+            if ($stream->getPeek()->isDelimiter(['='])) {
+                $namespace = null;
+                $stream->getNext();
+                $operator = '|=';
+            } else {
+                $namespace = $attribute;
+                $attribute = $stream->getNextIdentifier();
+                $operator = null;
+            }
+        } else {
+            $namespace = $operator = null;
+        }
+
+        if (null === $operator) {
+            $stream->skipWhitespace();
+            $next = $stream->getNext();
+
+            if ($next->isDelimiter([']'])) {
+                return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null);
+            } elseif ($next->isDelimiter(['='])) {
+                $operator = '=';
+            } elseif ($next->isDelimiter(['^', '$', '*', '~', '|', '!'])
+                && $stream->getPeek()->isDelimiter(['='])
+            ) {
+                $operator = $next->getValue().'=';
+                $stream->getNext();
+            } else {
+                throw SyntaxErrorException::unexpectedToken('operator', $next);
+            }
+        }
+
+        $stream->skipWhitespace();
+        $value = $stream->getNext();
+
+        if ($value->isNumber()) {
+            // if the value is a number, it's casted into a string
+            $value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition());
+        }
+
+        if (!($value->isIdentifier() || $value->isString())) {
+            throw SyntaxErrorException::unexpectedToken('string or identifier', $value);
+        }
+
+        $stream->skipWhitespace();
+        $next = $stream->getNext();
+
+        if (!$next->isDelimiter([']'])) {
+            throw SyntaxErrorException::unexpectedToken('"]"', $next);
+        }
+
+        return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue());
+    }
+}

+ 34 - 0
packer/deps/vendor/symfony/css-selector/Parser/ParserInterface.php

@@ -0,0 +1,34 @@
+<?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\CssSelector\Parser;
+
+use Symfony\Component\CssSelector\Node\SelectorNode;
+
+/**
+ * CSS selector parser interface.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+interface ParserInterface
+{
+    /**
+     * Parses given selector source into an array of tokens.
+     *
+     * @return SelectorNode[]
+     */
+    public function parse(string $source): array;
+}

+ 86 - 0
packer/deps/vendor/symfony/css-selector/Parser/Reader.php

@@ -0,0 +1,86 @@
+<?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\CssSelector\Parser;
+
+/**
+ * CSS selector reader.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class Reader
+{
+    private $source;
+    private $length;
+    private $position = 0;
+
+    public function __construct(string $source)
+    {
+        $this->source = $source;
+        $this->length = \strlen($source);
+    }
+
+    public function isEOF(): bool
+    {
+        return $this->position >= $this->length;
+    }
+
+    public function getPosition(): int
+    {
+        return $this->position;
+    }
+
+    public function getRemainingLength(): int
+    {
+        return $this->length - $this->position;
+    }
+
+    public function getSubstring(int $length, int $offset = 0): string
+    {
+        return substr($this->source, $this->position + $offset, $length);
+    }
+
+    public function getOffset(string $string)
+    {
+        $position = strpos($this->source, $string, $this->position);
+
+        return false === $position ? false : $position - $this->position;
+    }
+
+    /**
+     * @return array|false
+     */
+    public function findPattern(string $pattern)
+    {
+        $source = substr($this->source, $this->position);
+
+        if (preg_match($pattern, $source, $matches)) {
+            return $matches;
+        }
+
+        return false;
+    }
+
+    public function moveForward(int $length)
+    {
+        $this->position += $length;
+    }
+
+    public function moveToEnd()
+    {
+        $this->position = $this->length;
+    }
+}

+ 51 - 0
packer/deps/vendor/symfony/css-selector/Parser/Shortcut/ClassParser.php

@@ -0,0 +1,51 @@
+<?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\CssSelector\Parser\Shortcut;
+
+use Symfony\Component\CssSelector\Node\ClassNode;
+use Symfony\Component\CssSelector\Node\ElementNode;
+use Symfony\Component\CssSelector\Node\SelectorNode;
+use Symfony\Component\CssSelector\Parser\ParserInterface;
+
+/**
+ * CSS selector class parser shortcut.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class ClassParser implements ParserInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function parse(string $source): array
+    {
+        // Matches an optional namespace, optional element, and required class
+        // $source = 'test|input.ab6bd_field';
+        // $matches = array (size=4)
+        //     0 => string 'test|input.ab6bd_field' (length=22)
+        //     1 => string 'test' (length=4)
+        //     2 => string 'input' (length=5)
+        //     3 => string 'ab6bd_field' (length=11)
+        if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+\.([\w-]++)$/i', trim($source), $matches)) {
+            return [
+                new SelectorNode(new ClassNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])),
+            ];
+        }
+
+        return [];
+    }
+}

+ 47 - 0
packer/deps/vendor/symfony/css-selector/Parser/Shortcut/ElementParser.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\CssSelector\Parser\Shortcut;
+
+use Symfony\Component\CssSelector\Node\ElementNode;
+use Symfony\Component\CssSelector\Node\SelectorNode;
+use Symfony\Component\CssSelector\Parser\ParserInterface;
+
+/**
+ * CSS selector element parser shortcut.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class ElementParser implements ParserInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function parse(string $source): array
+    {
+        // Matches an optional namespace, required element or `*`
+        // $source = 'testns|testel';
+        // $matches = array (size=3)
+        //     0 => string 'testns|testel' (length=13)
+        //     1 => string 'testns' (length=6)
+        //     2 => string 'testel' (length=6)
+        if (preg_match('/^(?:([a-z]++)\|)?([\w-]++|\*)$/i', trim($source), $matches)) {
+            return [new SelectorNode(new ElementNode($matches[1] ?: null, $matches[2]))];
+        }
+
+        return [];
+    }
+}

+ 46 - 0
packer/deps/vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.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\CssSelector\Parser\Shortcut;
+
+use Symfony\Component\CssSelector\Node\ElementNode;
+use Symfony\Component\CssSelector\Node\SelectorNode;
+use Symfony\Component\CssSelector\Parser\ParserInterface;
+
+/**
+ * CSS selector class parser shortcut.
+ *
+ * This shortcut ensure compatibility with previous version.
+ * - The parser fails to parse an empty string.
+ * - In the previous version, an empty string matches each tags.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class EmptyStringParser implements ParserInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function parse(string $source): array
+    {
+        // Matches an empty string
+        if ('' == $source) {
+            return [new SelectorNode(new ElementNode(null, '*'))];
+        }
+
+        return [];
+    }
+}

+ 51 - 0
packer/deps/vendor/symfony/css-selector/Parser/Shortcut/HashParser.php

@@ -0,0 +1,51 @@
+<?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\CssSelector\Parser\Shortcut;
+
+use Symfony\Component\CssSelector\Node\ElementNode;
+use Symfony\Component\CssSelector\Node\HashNode;
+use Symfony\Component\CssSelector\Node\SelectorNode;
+use Symfony\Component\CssSelector\Parser\ParserInterface;
+
+/**
+ * CSS selector hash parser shortcut.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class HashParser implements ParserInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function parse(string $source): array
+    {
+        // Matches an optional namespace, optional element, and required id
+        // $source = 'test|input#ab6bd_field';
+        // $matches = array (size=4)
+        //     0 => string 'test|input#ab6bd_field' (length=22)
+        //     1 => string 'test' (length=4)
+        //     2 => string 'input' (length=5)
+        //     3 => string 'ab6bd_field' (length=11)
+        if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+#([\w-]++)$/i', trim($source), $matches)) {
+            return [
+                new SelectorNode(new HashNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])),
+            ];
+        }
+
+        return [];
+    }
+}

+ 111 - 0
packer/deps/vendor/symfony/css-selector/Parser/Token.php

@@ -0,0 +1,111 @@
+<?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\CssSelector\Parser;
+
+/**
+ * CSS selector token.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class Token
+{
+    public const TYPE_FILE_END = 'eof';
+    public const TYPE_DELIMITER = 'delimiter';
+    public const TYPE_WHITESPACE = 'whitespace';
+    public const TYPE_IDENTIFIER = 'identifier';
+    public const TYPE_HASH = 'hash';
+    public const TYPE_NUMBER = 'number';
+    public const TYPE_STRING = 'string';
+
+    private $type;
+    private $value;
+    private $position;
+
+    public function __construct(?string $type, ?string $value, ?int $position)
+    {
+        $this->type = $type;
+        $this->value = $value;
+        $this->position = $position;
+    }
+
+    public function getType(): ?int
+    {
+        return $this->type;
+    }
+
+    public function getValue(): ?string
+    {
+        return $this->value;
+    }
+
+    public function getPosition(): ?int
+    {
+        return $this->position;
+    }
+
+    public function isFileEnd(): bool
+    {
+        return self::TYPE_FILE_END === $this->type;
+    }
+
+    public function isDelimiter(array $values = []): bool
+    {
+        if (self::TYPE_DELIMITER !== $this->type) {
+            return false;
+        }
+
+        if (empty($values)) {
+            return true;
+        }
+
+        return \in_array($this->value, $values);
+    }
+
+    public function isWhitespace(): bool
+    {
+        return self::TYPE_WHITESPACE === $this->type;
+    }
+
+    public function isIdentifier(): bool
+    {
+        return self::TYPE_IDENTIFIER === $this->type;
+    }
+
+    public function isHash(): bool
+    {
+        return self::TYPE_HASH === $this->type;
+    }
+
+    public function isNumber(): bool
+    {
+        return self::TYPE_NUMBER === $this->type;
+    }
+
+    public function isString(): bool
+    {
+        return self::TYPE_STRING === $this->type;
+    }
+
+    public function __toString(): string
+    {
+        if ($this->value) {
+            return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position);
+        }
+
+        return sprintf('<%s at %s>', $this->type, $this->position);
+    }
+}

+ 171 - 0
packer/deps/vendor/symfony/css-selector/Parser/TokenStream.php

@@ -0,0 +1,171 @@
+<?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\CssSelector\Parser;
+
+use Symfony\Component\CssSelector\Exception\InternalErrorException;
+use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
+
+/**
+ * CSS selector token stream.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class TokenStream
+{
+    /**
+     * @var Token[]
+     */
+    private $tokens = [];
+
+    /**
+     * @var Token[]
+     */
+    private $used = [];
+
+    /**
+     * @var int
+     */
+    private $cursor = 0;
+
+    /**
+     * @var Token|null
+     */
+    private $peeked;
+
+    /**
+     * @var bool
+     */
+    private $peeking = false;
+
+    /**
+     * Pushes a token.
+     *
+     * @return $this
+     */
+    public function push(Token $token): self
+    {
+        $this->tokens[] = $token;
+
+        return $this;
+    }
+
+    /**
+     * Freezes stream.
+     *
+     * @return $this
+     */
+    public function freeze(): self
+    {
+        return $this;
+    }
+
+    /**
+     * Returns next token.
+     *
+     * @throws InternalErrorException If there is no more token
+     */
+    public function getNext(): Token
+    {
+        if ($this->peeking) {
+            $this->peeking = false;
+            $this->used[] = $this->peeked;
+
+            return $this->peeked;
+        }
+
+        if (!isset($this->tokens[$this->cursor])) {
+            throw new InternalErrorException('Unexpected token stream end.');
+        }
+
+        return $this->tokens[$this->cursor++];
+    }
+
+    /**
+     * Returns peeked token.
+     */
+    public function getPeek(): Token
+    {
+        if (!$this->peeking) {
+            $this->peeked = $this->getNext();
+            $this->peeking = true;
+        }
+
+        return $this->peeked;
+    }
+
+    /**
+     * Returns used tokens.
+     *
+     * @return Token[]
+     */
+    public function getUsed(): array
+    {
+        return $this->used;
+    }
+
+    /**
+     * Returns nex identifier token.
+     *
+     * @return string The identifier token value
+     *
+     * @throws SyntaxErrorException If next token is not an identifier
+     */
+    public function getNextIdentifier(): string
+    {
+        $next = $this->getNext();
+
+        if (!$next->isIdentifier()) {
+            throw SyntaxErrorException::unexpectedToken('identifier', $next);
+        }
+
+        return $next->getValue();
+    }
+
+    /**
+     * Returns nex identifier or star delimiter token.
+     *
+     * @return string|null The identifier token value or null if star found
+     *
+     * @throws SyntaxErrorException If next token is not an identifier or a star delimiter
+     */
+    public function getNextIdentifierOrStar(): ?string
+    {
+        $next = $this->getNext();
+
+        if ($next->isIdentifier()) {
+            return $next->getValue();
+        }
+
+        if ($next->isDelimiter(['*'])) {
+            return null;
+        }
+
+        throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next);
+    }
+
+    /**
+     * Skips next whitespace if any.
+     */
+    public function skipWhitespace()
+    {
+        $peek = $this->getPeek();
+
+        if ($peek->isWhitespace()) {
+            $this->getNext();
+        }
+    }
+}

+ 73 - 0
packer/deps/vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php

@@ -0,0 +1,73 @@
+<?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\CssSelector\Parser\Tokenizer;
+
+use Symfony\Component\CssSelector\Parser\Handler;
+use Symfony\Component\CssSelector\Parser\Reader;
+use Symfony\Component\CssSelector\Parser\Token;
+use Symfony\Component\CssSelector\Parser\TokenStream;
+
+/**
+ * CSS selector tokenizer.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class Tokenizer
+{
+    /**
+     * @var Handler\HandlerInterface[]
+     */
+    private $handlers;
+
+    public function __construct()
+    {
+        $patterns = new TokenizerPatterns();
+        $escaping = new TokenizerEscaping($patterns);
+
+        $this->handlers = [
+            new Handler\WhitespaceHandler(),
+            new Handler\IdentifierHandler($patterns, $escaping),
+            new Handler\HashHandler($patterns, $escaping),
+            new Handler\StringHandler($patterns, $escaping),
+            new Handler\NumberHandler($patterns),
+            new Handler\CommentHandler(),
+        ];
+    }
+
+    /**
+     * Tokenize selector source code.
+     */
+    public function tokenize(Reader $reader): TokenStream
+    {
+        $stream = new TokenStream();
+
+        while (!$reader->isEOF()) {
+            foreach ($this->handlers as $handler) {
+                if ($handler->handle($reader, $stream)) {
+                    continue 2;
+                }
+            }
+
+            $stream->push(new Token(Token::TYPE_DELIMITER, $reader->getSubstring(1), $reader->getPosition()));
+            $reader->moveForward(1);
+        }
+
+        return $stream
+            ->push(new Token(Token::TYPE_FILE_END, null, $reader->getPosition()))
+            ->freeze();
+    }
+}

+ 65 - 0
packer/deps/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php

@@ -0,0 +1,65 @@
+<?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\CssSelector\Parser\Tokenizer;
+
+/**
+ * CSS selector tokenizer escaping applier.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class TokenizerEscaping
+{
+    private $patterns;
+
+    public function __construct(TokenizerPatterns $patterns)
+    {
+        $this->patterns = $patterns;
+    }
+
+    public function escapeUnicode(string $value): string
+    {
+        $value = $this->replaceUnicodeSequences($value);
+
+        return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value);
+    }
+
+    public function escapeUnicodeAndNewLine(string $value): string
+    {
+        $value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value);
+
+        return $this->escapeUnicode($value);
+    }
+
+    private function replaceUnicodeSequences(string $value): string
+    {
+        return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) {
+            $c = hexdec($match[1]);
+
+            if (0x80 > $c %= 0x200000) {
+                return \chr($c);
+            }
+            if (0x800 > $c) {
+                return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
+            }
+            if (0x10000 > $c) {
+                return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
+            }
+
+            return '';
+        }, $value);
+    }
+}

+ 89 - 0
packer/deps/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php

@@ -0,0 +1,89 @@
+<?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\CssSelector\Parser\Tokenizer;
+
+/**
+ * CSS selector tokenizer patterns builder.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class TokenizerPatterns
+{
+    private $unicodeEscapePattern;
+    private $simpleEscapePattern;
+    private $newLineEscapePattern;
+    private $escapePattern;
+    private $stringEscapePattern;
+    private $nonAsciiPattern;
+    private $nmCharPattern;
+    private $nmStartPattern;
+    private $identifierPattern;
+    private $hashPattern;
+    private $numberPattern;
+    private $quotedStringPattern;
+
+    public function __construct()
+    {
+        $this->unicodeEscapePattern = '\\\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?';
+        $this->simpleEscapePattern = '\\\\(.)';
+        $this->newLineEscapePattern = '\\\\(?:\n|\r\n|\r|\f)';
+        $this->escapePattern = $this->unicodeEscapePattern.'|\\\\[^\n\r\f0-9a-f]';
+        $this->stringEscapePattern = $this->newLineEscapePattern.'|'.$this->escapePattern;
+        $this->nonAsciiPattern = '[^\x00-\x7F]';
+        $this->nmCharPattern = '[_a-z0-9-]|'.$this->escapePattern.'|'.$this->nonAsciiPattern;
+        $this->nmStartPattern = '[_a-z]|'.$this->escapePattern.'|'.$this->nonAsciiPattern;
+        $this->identifierPattern = '-?(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*';
+        $this->hashPattern = '#((?:'.$this->nmCharPattern.')+)';
+        $this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)';
+        $this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*';
+    }
+
+    public function getNewLineEscapePattern(): string
+    {
+        return '~^'.$this->newLineEscapePattern.'~';
+    }
+
+    public function getSimpleEscapePattern(): string
+    {
+        return '~^'.$this->simpleEscapePattern.'~';
+    }
+
+    public function getUnicodeEscapePattern(): string
+    {
+        return '~^'.$this->unicodeEscapePattern.'~i';
+    }
+
+    public function getIdentifierPattern(): string
+    {
+        return '~^'.$this->identifierPattern.'~i';
+    }
+
+    public function getHashPattern(): string
+    {
+        return '~^'.$this->hashPattern.'~i';
+    }
+
+    public function getNumberPattern(): string
+    {
+        return '~^'.$this->numberPattern.'~';
+    }
+
+    public function getQuotedStringPattern(string $quote): string
+    {
+        return '~^'.sprintf($this->quotedStringPattern, $quote).'~i';
+    }
+}

+ 20 - 0
packer/deps/vendor/symfony/css-selector/README.md

@@ -0,0 +1,20 @@
+CssSelector Component
+=====================
+
+The CssSelector component converts CSS selectors to XPath expressions.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/css_selector.html)
+  * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+  * [Report issues](https://github.com/symfony/symfony/issues) and
+    [send Pull Requests](https://github.com/symfony/symfony/pulls)
+    in the [main Symfony repository](https://github.com/symfony/symfony)
+
+Credits
+-------
+
+This component is a port of the Python cssselect library
+[v0.7.1](https://github.com/SimonSapin/cssselect/releases/tag/v0.7.1),
+which is distributed under the BSD license.

+ 65 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php

@@ -0,0 +1,65 @@
+<?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\CssSelector\XPath\Extension;
+
+/**
+ * XPath expression translator abstract extension.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+abstract class AbstractExtension implements ExtensionInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getNodeTranslators(): array
+    {
+        return [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCombinationTranslators(): array
+    {
+        return [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFunctionTranslators(): array
+    {
+        return [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPseudoClassTranslators(): array
+    {
+        return [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttributeMatchingTranslators(): array
+    {
+        return [];
+    }
+}

+ 119 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php

@@ -0,0 +1,119 @@
+<?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\CssSelector\XPath\Extension;
+
+use Symfony\Component\CssSelector\XPath\Translator;
+use Symfony\Component\CssSelector\XPath\XPathExpr;
+
+/**
+ * XPath expression translator attribute extension.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class AttributeMatchingExtension extends AbstractExtension
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttributeMatchingTranslators(): array
+    {
+        return [
+            'exists' => [$this, 'translateExists'],
+            '=' => [$this, 'translateEquals'],
+            '~=' => [$this, 'translateIncludes'],
+            '|=' => [$this, 'translateDashMatch'],
+            '^=' => [$this, 'translatePrefixMatch'],
+            '$=' => [$this, 'translateSuffixMatch'],
+            '*=' => [$this, 'translateSubstringMatch'],
+            '!=' => [$this, 'translateDifferent'],
+        ];
+    }
+
+    public function translateExists(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition($attribute);
+    }
+
+    public function translateEquals(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value)));
+    }
+
+    public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition($value ? sprintf(
+            '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)',
+            $attribute,
+            Translator::getXpathLiteral(' '.$value.' ')
+        ) : '0');
+    }
+
+    public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition(sprintf(
+            '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))',
+            $attribute,
+            Translator::getXpathLiteral($value),
+            Translator::getXpathLiteral($value.'-')
+        ));
+    }
+
+    public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition($value ? sprintf(
+            '%1$s and starts-with(%1$s, %2$s)',
+            $attribute,
+            Translator::getXpathLiteral($value)
+        ) : '0');
+    }
+
+    public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition($value ? sprintf(
+            '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s',
+            $attribute,
+            \strlen($value) - 1,
+            Translator::getXpathLiteral($value)
+        ) : '0');
+    }
+
+    public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition($value ? sprintf(
+            '%1$s and contains(%1$s, %2$s)',
+            $attribute,
+            Translator::getXpathLiteral($value)
+        ) : '0');
+    }
+
+    public function translateDifferent(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
+    {
+        return $xpath->addCondition(sprintf(
+            $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s',
+            $attribute,
+            Translator::getXpathLiteral($value)
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName(): string
+    {
+        return 'attribute-matching';
+    }
+}

+ 71 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php

@@ -0,0 +1,71 @@
+<?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\CssSelector\XPath\Extension;
+
+use Symfony\Component\CssSelector\XPath\XPathExpr;
+
+/**
+ * XPath expression translator combination extension.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class CombinationExtension extends AbstractExtension
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getCombinationTranslators(): array
+    {
+        return [
+            ' ' => [$this, 'translateDescendant'],
+            '>' => [$this, 'translateChild'],
+            '+' => [$this, 'translateDirectAdjacent'],
+            '~' => [$this, 'translateIndirectAdjacent'],
+        ];
+    }
+
+    public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
+    {
+        return $xpath->join('/descendant-or-self::*/', $combinedXpath);
+    }
+
+    public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
+    {
+        return $xpath->join('/', $combinedXpath);
+    }
+
+    public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
+    {
+        return $xpath
+            ->join('/following-sibling::', $combinedXpath)
+            ->addNameTest()
+            ->addCondition('position() = 1');
+    }
+
+    public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
+    {
+        return $xpath->join('/following-sibling::', $combinedXpath);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName(): string
+    {
+        return 'combination';
+    }
+}

+ 67 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php

@@ -0,0 +1,67 @@
+<?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\CssSelector\XPath\Extension;
+
+/**
+ * XPath expression translator extension interface.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+interface ExtensionInterface
+{
+    /**
+     * Returns node translators.
+     *
+     * These callables will receive the node as first argument and the translator as second argument.
+     *
+     * @return callable[]
+     */
+    public function getNodeTranslators(): array;
+
+    /**
+     * Returns combination translators.
+     *
+     * @return callable[]
+     */
+    public function getCombinationTranslators(): array;
+
+    /**
+     * Returns function translators.
+     *
+     * @return callable[]
+     */
+    public function getFunctionTranslators(): array;
+
+    /**
+     * Returns pseudo-class translators.
+     *
+     * @return callable[]
+     */
+    public function getPseudoClassTranslators(): array;
+
+    /**
+     * Returns attribute operation translators.
+     *
+     * @return callable[]
+     */
+    public function getAttributeMatchingTranslators(): array;
+
+    /**
+     * Returns extension name.
+     */
+    public function getName(): string;
+}

+ 171 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php

@@ -0,0 +1,171 @@
+<?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\CssSelector\XPath\Extension;
+
+use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
+use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
+use Symfony\Component\CssSelector\Node\FunctionNode;
+use Symfony\Component\CssSelector\Parser\Parser;
+use Symfony\Component\CssSelector\XPath\Translator;
+use Symfony\Component\CssSelector\XPath\XPathExpr;
+
+/**
+ * XPath expression translator function extension.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class FunctionExtension extends AbstractExtension
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getFunctionTranslators(): array
+    {
+        return [
+            'nth-child' => [$this, 'translateNthChild'],
+            'nth-last-child' => [$this, 'translateNthLastChild'],
+            'nth-of-type' => [$this, 'translateNthOfType'],
+            'nth-last-of-type' => [$this, 'translateNthLastOfType'],
+            'contains' => [$this, 'translateContains'],
+            'lang' => [$this, 'translateLang'],
+        ];
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr
+    {
+        try {
+            [$a, $b] = Parser::parseSeries($function->getArguments());
+        } catch (SyntaxErrorException $e) {
+            throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e);
+        }
+
+        $xpath->addStarPrefix();
+        if ($addNameTest) {
+            $xpath->addNameTest();
+        }
+
+        if (0 === $a) {
+            return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
+        }
+
+        if ($a < 0) {
+            if ($b < 1) {
+                return $xpath->addCondition('false()');
+            }
+
+            $sign = '<=';
+        } else {
+            $sign = '>=';
+        }
+
+        $expr = 'position()';
+
+        if ($last) {
+            $expr = 'last() - '.$expr;
+            --$b;
+        }
+
+        if (0 !== $b) {
+            $expr .= ' - '.$b;
+        }
+
+        $conditions = [sprintf('%s %s 0', $expr, $sign)];
+
+        if (1 !== $a && -1 !== $a) {
+            $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
+        }
+
+        return $xpath->addCondition(implode(' and ', $conditions));
+
+        // todo: handle an+b, odd, even
+        // an+b means every-a, plus b, e.g., 2n+1 means odd
+        // 0n+b means b
+        // n+0 means a=1, i.e., all elements
+        // an means every a elements, i.e., 2n means even
+        // -n means -1n
+        // -1n+6 means elements 6 and previous
+    }
+
+    public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr
+    {
+        return $this->translateNthChild($xpath, $function, true);
+    }
+
+    public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
+    {
+        return $this->translateNthChild($xpath, $function, false, false);
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
+    {
+        if ('*' === $xpath->getElement()) {
+            throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
+        }
+
+        return $this->translateNthChild($xpath, $function, true, false);
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr
+    {
+        $arguments = $function->getArguments();
+        foreach ($arguments as $token) {
+            if (!($token->isString() || $token->isIdentifier())) {
+                throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));
+            }
+        }
+
+        return $xpath->addCondition(sprintf(
+            'contains(string(.), %s)',
+            Translator::getXpathLiteral($arguments[0]->getValue())
+        ));
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
+    {
+        $arguments = $function->getArguments();
+        foreach ($arguments as $token) {
+            if (!($token->isString() || $token->isIdentifier())) {
+                throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
+            }
+        }
+
+        return $xpath->addCondition(sprintf(
+            'lang(%s)',
+            Translator::getXpathLiteral($arguments[0]->getValue())
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName(): string
+    {
+        return 'function';
+    }
+}

+ 187 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php

@@ -0,0 +1,187 @@
+<?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\CssSelector\XPath\Extension;
+
+use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
+use Symfony\Component\CssSelector\Node\FunctionNode;
+use Symfony\Component\CssSelector\XPath\Translator;
+use Symfony\Component\CssSelector\XPath\XPathExpr;
+
+/**
+ * XPath expression translator HTML extension.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class HtmlExtension extends AbstractExtension
+{
+    public function __construct(Translator $translator)
+    {
+        $translator
+            ->getExtension('node')
+            ->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true)
+            ->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPseudoClassTranslators(): array
+    {
+        return [
+            'checked' => [$this, 'translateChecked'],
+            'link' => [$this, 'translateLink'],
+            'disabled' => [$this, 'translateDisabled'],
+            'enabled' => [$this, 'translateEnabled'],
+            'selected' => [$this, 'translateSelected'],
+            'invalid' => [$this, 'translateInvalid'],
+            'hover' => [$this, 'translateHover'],
+            'visited' => [$this, 'translateVisited'],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFunctionTranslators(): array
+    {
+        return [
+            'lang' => [$this, 'translateLang'],
+        ];
+    }
+
+    public function translateChecked(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition(
+            '(@checked '
+            ."and (name(.) = 'input' or name(.) = 'command')"
+            ."and (@type = 'checkbox' or @type = 'radio'))"
+        );
+    }
+
+    public function translateLink(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')");
+    }
+
+    public function translateDisabled(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition(
+            '('
+                .'@disabled and'
+                .'('
+                    ."(name(.) = 'input' and @type != 'hidden')"
+                    ." or name(.) = 'button'"
+                    ." or name(.) = 'select'"
+                    ." or name(.) = 'textarea'"
+                    ." or name(.) = 'command'"
+                    ." or name(.) = 'fieldset'"
+                    ." or name(.) = 'optgroup'"
+                    ." or name(.) = 'option'"
+                .')'
+            .') or ('
+                ."(name(.) = 'input' and @type != 'hidden')"
+                ." or name(.) = 'button'"
+                ." or name(.) = 'select'"
+                ." or name(.) = 'textarea'"
+            .')'
+            .' and ancestor::fieldset[@disabled]'
+        );
+        // todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any."
+    }
+
+    public function translateEnabled(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition(
+            '('
+                .'@href and ('
+                    ."name(.) = 'a'"
+                    ." or name(.) = 'link'"
+                    ." or name(.) = 'area'"
+                .')'
+            .') or ('
+                .'('
+                    ."name(.) = 'command'"
+                    ." or name(.) = 'fieldset'"
+                    ." or name(.) = 'optgroup'"
+                .')'
+                .' and not(@disabled)'
+            .') or ('
+                .'('
+                    ."(name(.) = 'input' and @type != 'hidden')"
+                    ." or name(.) = 'button'"
+                    ." or name(.) = 'select'"
+                    ." or name(.) = 'textarea'"
+                    ." or name(.) = 'keygen'"
+                .')'
+                .' and not (@disabled or ancestor::fieldset[@disabled])'
+            .') or ('
+                ."name(.) = 'option' and not("
+                    .'@disabled or ancestor::optgroup[@disabled]'
+                .')'
+            .')'
+        );
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
+    {
+        $arguments = $function->getArguments();
+        foreach ($arguments as $token) {
+            if (!($token->isString() || $token->isIdentifier())) {
+                throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
+            }
+        }
+
+        return $xpath->addCondition(sprintf(
+            'ancestor-or-self::*[@lang][1][starts-with(concat('
+            ."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')"
+            .', %s)]',
+            'lang',
+            Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-')
+        ));
+    }
+
+    public function translateSelected(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition("(@selected and name(.) = 'option')");
+    }
+
+    public function translateInvalid(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition('0');
+    }
+
+    public function translateHover(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition('0');
+    }
+
+    public function translateVisited(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition('0');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName(): string
+    {
+        return 'html';
+    }
+}

+ 197 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/NodeExtension.php

@@ -0,0 +1,197 @@
+<?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\CssSelector\XPath\Extension;
+
+use Symfony\Component\CssSelector\Node;
+use Symfony\Component\CssSelector\XPath\Translator;
+use Symfony\Component\CssSelector\XPath\XPathExpr;
+
+/**
+ * XPath expression translator node extension.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class NodeExtension extends AbstractExtension
+{
+    public const ELEMENT_NAME_IN_LOWER_CASE = 1;
+    public const ATTRIBUTE_NAME_IN_LOWER_CASE = 2;
+    public const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4;
+
+    private $flags;
+
+    public function __construct(int $flags = 0)
+    {
+        $this->flags = $flags;
+    }
+
+    /**
+     * @return $this
+     */
+    public function setFlag(int $flag, bool $on): self
+    {
+        if ($on && !$this->hasFlag($flag)) {
+            $this->flags += $flag;
+        }
+
+        if (!$on && $this->hasFlag($flag)) {
+            $this->flags -= $flag;
+        }
+
+        return $this;
+    }
+
+    public function hasFlag(int $flag): bool
+    {
+        return (bool) ($this->flags & $flag);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNodeTranslators(): array
+    {
+        return [
+            'Selector' => [$this, 'translateSelector'],
+            'CombinedSelector' => [$this, 'translateCombinedSelector'],
+            'Negation' => [$this, 'translateNegation'],
+            'Function' => [$this, 'translateFunction'],
+            'Pseudo' => [$this, 'translatePseudo'],
+            'Attribute' => [$this, 'translateAttribute'],
+            'Class' => [$this, 'translateClass'],
+            'Hash' => [$this, 'translateHash'],
+            'Element' => [$this, 'translateElement'],
+        ];
+    }
+
+    public function translateSelector(Node\SelectorNode $node, Translator $translator): XPathExpr
+    {
+        return $translator->nodeToXPath($node->getTree());
+    }
+
+    public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator): XPathExpr
+    {
+        return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector());
+    }
+
+    public function translateNegation(Node\NegationNode $node, Translator $translator): XPathExpr
+    {
+        $xpath = $translator->nodeToXPath($node->getSelector());
+        $subXpath = $translator->nodeToXPath($node->getSubSelector());
+        $subXpath->addNameTest();
+
+        if ($subXpath->getCondition()) {
+            return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition()));
+        }
+
+        return $xpath->addCondition('0');
+    }
+
+    public function translateFunction(Node\FunctionNode $node, Translator $translator): XPathExpr
+    {
+        $xpath = $translator->nodeToXPath($node->getSelector());
+
+        return $translator->addFunction($xpath, $node);
+    }
+
+    public function translatePseudo(Node\PseudoNode $node, Translator $translator): XPathExpr
+    {
+        $xpath = $translator->nodeToXPath($node->getSelector());
+
+        return $translator->addPseudoClass($xpath, $node->getIdentifier());
+    }
+
+    public function translateAttribute(Node\AttributeNode $node, Translator $translator): XPathExpr
+    {
+        $name = $node->getAttribute();
+        $safe = $this->isSafeName($name);
+
+        if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) {
+            $name = strtolower($name);
+        }
+
+        if ($node->getNamespace()) {
+            $name = sprintf('%s:%s', $node->getNamespace(), $name);
+            $safe = $safe && $this->isSafeName($node->getNamespace());
+        }
+
+        $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name));
+        $value = $node->getValue();
+        $xpath = $translator->nodeToXPath($node->getSelector());
+
+        if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) {
+            $value = strtolower($value);
+        }
+
+        return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value);
+    }
+
+    public function translateClass(Node\ClassNode $node, Translator $translator): XPathExpr
+    {
+        $xpath = $translator->nodeToXPath($node->getSelector());
+
+        return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName());
+    }
+
+    public function translateHash(Node\HashNode $node, Translator $translator): XPathExpr
+    {
+        $xpath = $translator->nodeToXPath($node->getSelector());
+
+        return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId());
+    }
+
+    public function translateElement(Node\ElementNode $node): XPathExpr
+    {
+        $element = $node->getElement();
+
+        if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) {
+            $element = strtolower($element);
+        }
+
+        if ($element) {
+            $safe = $this->isSafeName($element);
+        } else {
+            $element = '*';
+            $safe = true;
+        }
+
+        if ($node->getNamespace()) {
+            $element = sprintf('%s:%s', $node->getNamespace(), $element);
+            $safe = $safe && $this->isSafeName($node->getNamespace());
+        }
+
+        $xpath = new XPathExpr('', $element);
+
+        if (!$safe) {
+            $xpath->addNameTest();
+        }
+
+        return $xpath;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName(): string
+    {
+        return 'node';
+    }
+
+    private function isSafeName(string $name): bool
+    {
+        return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name);
+    }
+}

+ 122 - 0
packer/deps/vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php

@@ -0,0 +1,122 @@
+<?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\CssSelector\XPath\Extension;
+
+use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
+use Symfony\Component\CssSelector\XPath\XPathExpr;
+
+/**
+ * XPath expression translator pseudo-class extension.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class PseudoClassExtension extends AbstractExtension
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getPseudoClassTranslators(): array
+    {
+        return [
+            'root' => [$this, 'translateRoot'],
+            'first-child' => [$this, 'translateFirstChild'],
+            'last-child' => [$this, 'translateLastChild'],
+            'first-of-type' => [$this, 'translateFirstOfType'],
+            'last-of-type' => [$this, 'translateLastOfType'],
+            'only-child' => [$this, 'translateOnlyChild'],
+            'only-of-type' => [$this, 'translateOnlyOfType'],
+            'empty' => [$this, 'translateEmpty'],
+        ];
+    }
+
+    public function translateRoot(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition('not(parent::*)');
+    }
+
+    public function translateFirstChild(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath
+            ->addStarPrefix()
+            ->addNameTest()
+            ->addCondition('position() = 1');
+    }
+
+    public function translateLastChild(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath
+            ->addStarPrefix()
+            ->addNameTest()
+            ->addCondition('position() = last()');
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function translateFirstOfType(XPathExpr $xpath): XPathExpr
+    {
+        if ('*' === $xpath->getElement()) {
+            throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
+        }
+
+        return $xpath
+            ->addStarPrefix()
+            ->addCondition('position() = 1');
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function translateLastOfType(XPathExpr $xpath): XPathExpr
+    {
+        if ('*' === $xpath->getElement()) {
+            throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
+        }
+
+        return $xpath
+            ->addStarPrefix()
+            ->addCondition('position() = last()');
+    }
+
+    public function translateOnlyChild(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath
+            ->addStarPrefix()
+            ->addNameTest()
+            ->addCondition('last() = 1');
+    }
+
+    public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
+    {
+        $element = $xpath->getElement();
+
+        return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
+    }
+
+    public function translateEmpty(XPathExpr $xpath): XPathExpr
+    {
+        return $xpath->addCondition('not(*) and not(string-length())');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName(): string
+    {
+        return 'pseudo-class';
+    }
+}

+ 230 - 0
packer/deps/vendor/symfony/css-selector/XPath/Translator.php

@@ -0,0 +1,230 @@
+<?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\CssSelector\XPath;
+
+use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
+use Symfony\Component\CssSelector\Node\FunctionNode;
+use Symfony\Component\CssSelector\Node\NodeInterface;
+use Symfony\Component\CssSelector\Node\SelectorNode;
+use Symfony\Component\CssSelector\Parser\Parser;
+use Symfony\Component\CssSelector\Parser\ParserInterface;
+
+/**
+ * XPath expression translator interface.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class Translator implements TranslatorInterface
+{
+    private $mainParser;
+
+    /**
+     * @var ParserInterface[]
+     */
+    private $shortcutParsers = [];
+
+    /**
+     * @var Extension\ExtensionInterface[]
+     */
+    private $extensions = [];
+
+    private $nodeTranslators = [];
+    private $combinationTranslators = [];
+    private $functionTranslators = [];
+    private $pseudoClassTranslators = [];
+    private $attributeMatchingTranslators = [];
+
+    public function __construct(ParserInterface $parser = null)
+    {
+        $this->mainParser = $parser ?: new Parser();
+
+        $this
+            ->registerExtension(new Extension\NodeExtension())
+            ->registerExtension(new Extension\CombinationExtension())
+            ->registerExtension(new Extension\FunctionExtension())
+            ->registerExtension(new Extension\PseudoClassExtension())
+            ->registerExtension(new Extension\AttributeMatchingExtension())
+        ;
+    }
+
+    public static function getXpathLiteral(string $element): string
+    {
+        if (false === strpos($element, "'")) {
+            return "'".$element."'";
+        }
+
+        if (false === strpos($element, '"')) {
+            return '"'.$element.'"';
+        }
+
+        $string = $element;
+        $parts = [];
+        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));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string
+    {
+        $selectors = $this->parseSelectors($cssExpr);
+
+        /** @var SelectorNode $selector */
+        foreach ($selectors as $index => $selector) {
+            if (null !== $selector->getPseudoElement()) {
+                throw new ExpressionErrorException('Pseudo-elements are not supported.');
+            }
+
+            $selectors[$index] = $this->selectorToXPath($selector, $prefix);
+        }
+
+        return implode(' | ', $selectors);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string
+    {
+        return ($prefix ?: '').$this->nodeToXPath($selector);
+    }
+
+    /**
+     * @return $this
+     */
+    public function registerExtension(Extension\ExtensionInterface $extension): self
+    {
+        $this->extensions[$extension->getName()] = $extension;
+
+        $this->nodeTranslators = array_merge($this->nodeTranslators, $extension->getNodeTranslators());
+        $this->combinationTranslators = array_merge($this->combinationTranslators, $extension->getCombinationTranslators());
+        $this->functionTranslators = array_merge($this->functionTranslators, $extension->getFunctionTranslators());
+        $this->pseudoClassTranslators = array_merge($this->pseudoClassTranslators, $extension->getPseudoClassTranslators());
+        $this->attributeMatchingTranslators = array_merge($this->attributeMatchingTranslators, $extension->getAttributeMatchingTranslators());
+
+        return $this;
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function getExtension(string $name): Extension\ExtensionInterface
+    {
+        if (!isset($this->extensions[$name])) {
+            throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name));
+        }
+
+        return $this->extensions[$name];
+    }
+
+    /**
+     * @return $this
+     */
+    public function registerParserShortcut(ParserInterface $shortcut): self
+    {
+        $this->shortcutParsers[] = $shortcut;
+
+        return $this;
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function nodeToXPath(NodeInterface $node): XPathExpr
+    {
+        if (!isset($this->nodeTranslators[$node->getNodeName()])) {
+            throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName()));
+        }
+
+        return $this->nodeTranslators[$node->getNodeName()]($node, $this);
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function addCombination(string $combiner, NodeInterface $xpath, NodeInterface $combinedXpath): XPathExpr
+    {
+        if (!isset($this->combinationTranslators[$combiner])) {
+            throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner));
+        }
+
+        return $this->combinationTranslators[$combiner]($this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath));
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function addFunction(XPathExpr $xpath, FunctionNode $function): XPathExpr
+    {
+        if (!isset($this->functionTranslators[$function->getName()])) {
+            throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName()));
+        }
+
+        return $this->functionTranslators[$function->getName()]($xpath, $function);
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function addPseudoClass(XPathExpr $xpath, string $pseudoClass): XPathExpr
+    {
+        if (!isset($this->pseudoClassTranslators[$pseudoClass])) {
+            throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass));
+        }
+
+        return $this->pseudoClassTranslators[$pseudoClass]($xpath);
+    }
+
+    /**
+     * @throws ExpressionErrorException
+     */
+    public function addAttributeMatching(XPathExpr $xpath, string $operator, string $attribute, $value): XPathExpr
+    {
+        if (!isset($this->attributeMatchingTranslators[$operator])) {
+            throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator));
+        }
+
+        return $this->attributeMatchingTranslators[$operator]($xpath, $attribute, $value);
+    }
+
+    /**
+     * @return SelectorNode[]
+     */
+    private function parseSelectors(string $css): array
+    {
+        foreach ($this->shortcutParsers as $shortcut) {
+            $tokens = $shortcut->parse($css);
+
+            if (!empty($tokens)) {
+                return $tokens;
+            }
+        }
+
+        return $this->mainParser->parse($css);
+    }
+}

+ 37 - 0
packer/deps/vendor/symfony/css-selector/XPath/TranslatorInterface.php

@@ -0,0 +1,37 @@
+<?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\CssSelector\XPath;
+
+use Symfony\Component\CssSelector\Node\SelectorNode;
+
+/**
+ * XPath expression translator interface.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+interface TranslatorInterface
+{
+    /**
+     * Translates a CSS selector to an XPath expression.
+     */
+    public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string;
+
+    /**
+     * Translates a parsed selector node to an XPath expression.
+     */
+    public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string;
+}

+ 102 - 0
packer/deps/vendor/symfony/css-selector/XPath/XPathExpr.php

@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\CssSelector\XPath;
+
+/**
+ * XPath expression translator interface.
+ *
+ * This component is a port of the Python cssselect library,
+ * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ *
+ * @internal
+ */
+class XPathExpr
+{
+    private $path;
+    private $element;
+    private $condition;
+
+    public function __construct(string $path = '', string $element = '*', string $condition = '', bool $starPrefix = false)
+    {
+        $this->path = $path;
+        $this->element = $element;
+        $this->condition = $condition;
+
+        if ($starPrefix) {
+            $this->addStarPrefix();
+        }
+    }
+
+    public function getElement(): string
+    {
+        return $this->element;
+    }
+
+    public function addCondition(string $condition): self
+    {
+        $this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition;
+
+        return $this;
+    }
+
+    public function getCondition(): string
+    {
+        return $this->condition;
+    }
+
+    public function addNameTest(): self
+    {
+        if ('*' !== $this->element) {
+            $this->addCondition('name() = '.Translator::getXpathLiteral($this->element));
+            $this->element = '*';
+        }
+
+        return $this;
+    }
+
+    public function addStarPrefix(): self
+    {
+        $this->path .= '*/';
+
+        return $this;
+    }
+
+    /**
+     * Joins another XPathExpr with a combiner.
+     *
+     * @return $this
+     */
+    public function join(string $combiner, self $expr): self
+    {
+        $path = $this->__toString().$combiner;
+
+        if ('*/' !== $expr->path) {
+            $path .= $expr->path;
+        }
+
+        $this->path = $path;
+        $this->element = $expr->element;
+        $this->condition = $expr->condition;
+
+        return $this;
+    }
+
+    public function __toString(): string
+    {
+        $path = $this->path.$this->element;
+        $condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']';
+
+        return $path.$condition;
+    }
+}

+ 32 - 0
packer/deps/vendor/symfony/css-selector/composer.json

@@ -0,0 +1,32 @@
+{
+    "name": "symfony/css-selector",
+    "type": "library",
+    "description": "Converts CSS selectors to XPath expressions",
+    "keywords": [],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Jean-François Simon",
+            "email": "jeanfrancois.simon@sensiolabs.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=7.2.5"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Component\\CssSelector\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "minimum-stability": "dev"
+}

+ 215 - 0
packer/deps/vendor/voku/html-min/CHANGELOG.md

@@ -0,0 +1,215 @@
+# Changelog 4.4.8 (2020-08-11) 
+
+- remove content before "<!doctype.*>", otherwise DOMDocument cannot handle the input
+- use a new version of "voku/simple_html_dom" (4.7.22)
+
+# Changelog 4.4.7 (2020-08-11) 
+
+- use a new version of "voku/simple_html_dom" (4.7.21)
+
+# Changelog 4.4.6 (2020-08-08)
+
+-  fix invalid input html
+
+# Changelog 4.4.5 (2020-08-06)
+
+- allow to configure special comments via "setSpecialHtmlComments()"
+
+# Changelog 4.4.4 (2020-08-06)
+
+- fix problems with self-closing-tags e.g. <wbr>
+
+# Changelog 4.4.3 (2020-04-06)
+
+- fix "domNodeClosingTagOptional()" -> fix logic of detecting next sibling dom node
+
+# Changelog 4.4.2 (2020-04-06)
+
+- fix "domNodeClosingTagOptional()" -> do not remove "</p>" if there is more content in the parent node
+
+# Changelog 4.4.1 (2020-04-05) 
+
+- use a new version of "voku/simple_html_dom" (4.7.16)
+
+# Changelog 4.4.0 (2020-04-05)
+
+- add support for removing more default attributes
+- add "doRemoveDefaultTypeFromButton()"
+- add "doRemoveDefaultMediaTypeFromStyleAndLinkTag()"
+- add "doRemoveDeprecatedTypeFromStyleAndLinkTag()"
+- add "overwriteTemplateLogicSyntaxInSpecialScriptTags()"
+- use a new version of "voku/simple_html_dom" (4.7.15)
+
+# Changelog 4.3.0 (2020-03-22)
+
+- add "isHTML4()"
+- add "isXHTML()"
+- fix "remove deprecated script-mime-types"
+- use a new version of "voku/simple_html_dom" (4.7.13)
+
+
+# Changelog 4.2.0 (2020-03-06)
+
+- add "doKeepHttpAndHttpsPrefixOnExternalAttributes(bool)": keep http:// and https:// prefix for external links | thanks @abuyoyo
+- add "doMakeSameDomainsLinksRelative(string[] $localDomains)": make the local domains relative | thanks @abuyoyo
+- optimized "optgroup"-html compressing
+- use a new version of "voku/simple_html_dom" (4.7.12)
+
+
+# Changelog 4.1.0 (2020-02-06)
+
+- add "doRemoveHttpsPrefixFromAttributes()": remove optional "https:"-prefix from attributes (depends on "doOptimizeAttributes(true)")
+
+
+# Changelog 4.0.7 (2019-11-18)
+
+- fix: too many single white spaces are removed
+
+
+# Changelog 4.0.6 (2019-10-27)
+
+- fix: fix regex for self-closing tags
+- optimize performance via "strpos" before regex
+
+
+# Changelog 4.0.5 (2019-09-19)
+
+- fix: protect "nocompress"-tags before notifying the Observer
+
+
+# Changelog 4.0.4 (2019-09-17)
+
+- fix: removing of dom elements
+
+
+# Changelog 4.0.3
+
+- fix: removing of "\</p>"-tags
+
+
+# Changelog 4.0.2
+
+- use new version of "voku/simple_html_dom"
+
+
+# Changelog 4.0.1
+
+- optimize unicode support
+- fix: remove unnecessary \</source> closing tag #40
+- fix: bad minify text/x-custom-template #38
+
+
+# Changelog 4.0.0
+
+- use interfaces in the "HtmlMinDom"-Observer
+
+-> this is a BC, but you can simply replace this classes in your observer implementation:
+
+---> "SimpleHtmlDom" with "SimpleHtmlDomInterface
+
+---> "HtmlMin" with "HtmlMinInterface"   
+
+
+# Changelog 3.1.8
+
+- fix / optimize: "doRemoveOmittedQuotes" -> support for "\<html ⚡>" via SimpleHtmlDom
+
+
+# Changelog 3.1.7
+
+- fix: "'" && '"' in attributes
+
+
+# Changelog 3.1.6
+
+- fix: keep HTML closing tags in \<script> tags 
+
+
+# Changelog 3.1.5
+
+- fix: keep newlines in e.g. "pre"-tags
+- fix: remove newlines from "srcset" and "sizes" attribute
+
+
+# Changelog 3.1.4 (2019-02-28)
+
+- fix: get parent node
+- code-style: remove "true" && "false" if return type is bool
+
+
+# Changelog 3.1.1 / 3.1.2 / 3.1.3 (2018-12-28)
+
+- use new version of "voku/simple_html_dom"
+
+
+# Changelog 3.1.0 (2018-12-27)
+
+- add "HtmlMinDomObserverInterface" (+ HtmlMin as Observable)
+- use phpcs fixer
+
+
+# Changelog 3.0.6 (2018-12-01)
+
+- implement the "\<nocompress>"-tag + tests
+
+
+# Changelog 3.0.5 (2018-10-17)
+
+- update vendor (voku/simple_html_dom >= v4.1.7) + fix entities (&lt;, &gt;)
+
+
+# Changelog 3.0.4 (2018-10-07)
+
+- update vendor (voku/simple_html_dom >= v4.1.6) + option for keep broken html
+
+
+# Changelog 3.0.3 (2018-05-08)
+
+- update vendor (voku/simple_html_dom >= v4.1.4)
+
+
+# Changelog 3.0.2 (2018-02-12)
+
+- fix regex for self-closing tags
+
+
+# Changelog 3.0.1 (2017-12-29)
+
+- update vendor (voku/simple_html_dom >= v4.1.3)
+
+
+# Changelog 3.0.0 (2017-12-22)
+
+- remove "Portable UTF-8" as required dependency
+
+-> this is a breaking change, without any API changes
+
+
+# Changelog 2.0.4 (2017-12-22)
+
+- check if there was already whitespace e.g. from the content
+
+
+# Changelog 2.0.3 (2017-12-22)
+
+- fix "Minifier removes spaces between tags"
+- fix "Multiple horizontal whitespace characters not collapsed"
+
+
+# Changelog 2.0.2 (2017-12-10)
+
+- try to fix "Minifier removes spaces between tags" v2
+- disable "doRemoveWhitespaceAroundTags" by default
+
+
+# Changelog 2.0.1 (2017-12-10)
+
+- try to fix "Minifier removes spaces between tags" v1
+
+
+# Changelog 2.0.0 (2017-12-03)
+
+- drop support for PHP < 7.0
+- use "strict_types"
+- doRemoveOmittedQuotes() -> remove quotes e.g. class="lall" => class=lall
+- doRemoveOmittedHtmlTags() -> remove ommitted html tags e.g. \<p>lall\</p> => \<p>lall 

+ 21 - 0
packer/deps/vendor/voku/html-min/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 
+
+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.

+ 118 - 0
packer/deps/vendor/voku/html-min/README.md

@@ -0,0 +1,118 @@
+[![Build Status](https://travis-ci.org/voku/HtmlMin.svg?branch=master)](https://travis-ci.org/voku/HtmlMin)
+[![Coverage Status](https://coveralls.io/repos/github/voku/HtmlMin/badge.svg?branch=master)](https://coveralls.io/github/voku/HtmlMin?branch=master)
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a433ed2b3b7546b3a1c520310222a601)](https://www.codacy.com/app/voku/HtmlMin?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=voku/HtmlMin&amp;utm_campaign=Badge_Grade)
+[![Latest Stable Version](https://poser.pugx.org/voku/html-min/v/stable)](https://packagist.org/packages/voku/html-min) 
+[![Total Downloads](https://poser.pugx.org/voku/html-min/downloads)](https://packagist.org/packages/voku/html-min) 
+[![License](https://poser.pugx.org/voku/html-min/license)](https://packagist.org/packages/voku/html-min)
+[![Donate to this project using Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/moelleken)
+[![Donate to this project using Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://www.patreon.com/voku)
+
+# :clamp: HtmlMin: HTML Compressor and Minifier for PHP
+
+### Description
+
+HtmlMin is a fast and very easy to use PHP library that minifies given HTML5 source by removing extra whitespaces, comments and other unneeded characters without breaking the content structure. As a result pages become smaller in size and load faster. It will also prepare the HTML for better gzip results, by re-ranging (sort alphabetical) attributes and css-class-names.
+
+
+### Install via "composer require"
+
+```shell
+composer require voku/html-min
+```
+
+### Quick Start
+
+```php
+use voku\helper\HtmlMin;
+
+$html = "
+<html>
+  \r\n\t
+  <body>
+    <ul style=''>
+      <li style='display: inline;' class='foo'>
+        \xc3\xa0
+      </li>
+      <li class='foo' style='display: inline;'>
+        \xc3\xa1
+      </li>
+    </ul>
+  </body>
+  \r\n\t
+</html>
+";
+$htmlMin = new HtmlMin();
+
+echo $htmlMin->minify($html); 
+// '<html><body><ul><li class=foo style="display: inline;"> à <li class=foo style="display: inline;"> á </ul>'
+```
+
+### Options
+
+```php
+use voku\helper\HtmlMin;
+
+$htmlMin = new HtmlMin();
+
+/* 
+ * Protected HTML (inline css / inline js / conditional comments) are still protected,
+ *    no matter what settings you use.
+ */
+
+$htmlMin->doOptimizeViaHtmlDomParser();               // optimize html via "HtmlDomParser()"
+$htmlMin->doRemoveComments();                         // remove default HTML comments (depends on "doOptimizeViaHtmlDomParser(true)")
+$htmlMin->doSumUpWhitespace();                        // sum-up extra whitespace from the Dom (depends on "doOptimizeViaHtmlDomParser(true)")
+$htmlMin->doRemoveWhitespaceAroundTags();             // remove whitespace around tags (depends on "doOptimizeViaHtmlDomParser(true)")
+$htmlMin->doOptimizeAttributes();                     // optimize html attributes (depends on "doOptimizeViaHtmlDomParser(true)")
+$htmlMin->doRemoveHttpPrefixFromAttributes();         // remove optional "http:"-prefix from attributes (depends on "doOptimizeAttributes(true)")
+$htmlMin->doRemoveHttpsPrefixFromAttributes();        // remove optional "https:"-prefix from attributes (depends on "doOptimizeAttributes(true)")
+$htmlMin->doKeepHttpAndHttpsPrefixOnExternalAttributes(); // keep "http:"- and "https:"-prefix for all external links 
+$htmlMin->doMakeSameDomainsLinksRelative(['example.com']); // make some links relative, by removing the domain from attributes
+$htmlMin->doRemoveDefaultAttributes();                // remove defaults (depends on "doOptimizeAttributes(true)" | disabled by default)
+$htmlMin->doRemoveDeprecatedAnchorName();             // remove deprecated anchor-jump (depends on "doOptimizeAttributes(true)")
+$htmlMin->doRemoveDeprecatedScriptCharsetAttribute(); // remove deprecated charset-attribute - the browser will use the charset from the HTTP-Header, anyway (depends on "doOptimizeAttributes(true)")
+$htmlMin->doRemoveDeprecatedTypeFromScriptTag();      // remove deprecated script-mime-types (depends on "doOptimizeAttributes(true)")
+$htmlMin->doRemoveDeprecatedTypeFromStylesheetLink(); // remove "type=text/css" for css links (depends on "doOptimizeAttributes(true)")
+$htmlMin->doRemoveDeprecatedTypeFromStyleAndLinkTag(); // remove "type=text/css" from all links and styles
+$htmlMin->doRemoveDefaultMediaTypeFromStyleAndLinkTag(); // remove "media="all" from all links and styles
+$htmlMin->doRemoveDefaultTypeFromButton();            // remove type="submit" from button tags 
+$htmlMin->doRemoveEmptyAttributes();                  // remove some empty attributes (depends on "doOptimizeAttributes(true)")
+$htmlMin->doRemoveValueFromEmptyInput();              // remove 'value=""' from empty <input> (depends on "doOptimizeAttributes(true)")
+$htmlMin->doSortCssClassNames();                      // sort css-class-names, for better gzip results (depends on "doOptimizeAttributes(true)")
+$htmlMin->doSortHtmlAttributes();                     // sort html-attributes, for better gzip results (depends on "doOptimizeAttributes(true)")
+$htmlMin->doRemoveSpacesBetweenTags();                // remove more (aggressive) spaces in the dom (disabled by default)
+$htmlMin->doRemoveOmittedQuotes();                    // remove quotes e.g. class="lall" => class=lall
+$htmlMin->doRemoveOmittedHtmlTags();                  // remove ommitted html tags e.g. <p>lall</p> => <p>lall 
+```
+
+PS: you can use the "nocompress"-tag to keep the html e.g.: "<nocompress>\n foobar \n</nocompress>"
+
+### Unit Test
+
+1) [Composer](https://getcomposer.org) is a prerequisite for running the tests.
+
+```
+composer install voku/html-min
+```
+
+2) The tests can be executed by running this command from the root directory:
+
+```bash
+./vendor/bin/phpunit
+```
+
+### Support
+
+For support and donations please visit [Github](https://github.com/voku/HtmlMin/) | [Issues](https://github.com/voku/HtmlMin/issues) | [PayPal](https://paypal.me/moelleken) | [Patreon](https://www.patreon.com/voku).
+
+For status updates and release announcements please visit [Releases](https://github.com/voku/HtmlMin/releases) | [Twitter](https://twitter.com/suckup_de) | [Patreon](https://www.patreon.com/voku/posts).
+
+For professional support please contact [me](https://about.me/voku).
+
+### Thanks
+
+- Thanks to [GitHub](https://github.com) (Microsoft) for hosting the code and a good infrastructure including Issues-Managment, etc.
+- Thanks to [IntelliJ](https://www.jetbrains.com) as they make the best IDEs for PHP and they gave me an open source license for PhpStorm!
+- Thanks to [Travis CI](https://travis-ci.com/) for being the most awesome, easiest continous integration tool out there!
+- Thanks to [StyleCI](https://styleci.io/) for the simple but powerfull code style check.
+- Thanks to [PHPStan](https://github.com/phpstan/phpstan) && [Psalm](https://github.com/vimeo/psalm) for relly great Static analysis tools and for discover bugs in the code!

Some files were not shown because too many files changed in this diff