فهرست منبع

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

DSR! 4 سال پیش
والد
کامیت
72280a1c45
100فایلهای تغییر یافته به همراه8627 افزوده شده و 0 حذف شده
  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
+{
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 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!

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است