Updated 3rd party dependencies
authorAlexander Ebert <ebert@woltlab.com>
Tue, 26 Nov 2019 15:32:02 +0000 (16:32 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 26 Nov 2019 15:32:02 +0000 (16:32 +0100)
46 files changed:
wcfsetup/install/files/lib/system/api/bin/pscss
wcfsetup/install/files/lib/system/api/bin/pscss.bat [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer.json
wcfsetup/install/files/lib/system/api/composer.lock
wcfsetup/install/files/lib/system/api/composer/installed.json
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/NEWS
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/README.md
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/VERSION
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/library/HTMLPurifier.includes.php
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/library/HTMLPurifier.php
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/library/HTMLPurifier/Config.php
wcfsetup/install/files/lib/system/api/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/bin/pscss
wcfsetup/install/files/lib/system/api/leafo/scssphp/example/Server.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/scss.inc.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/Block.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/Compiler.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/Compiler/Environment.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/Parser.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/Base64.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/Base64VLQ.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/Base64VLQEncoder.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/SourceMapGenerator.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/Util.php
wcfsetup/install/files/lib/system/api/leafo/scssphp/src/Version.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Exception/SyntaxErrorException.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/AttributeNode.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/ClassNode.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/CombinedSelectorNode.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/FunctionNode.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/HashNode.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/PseudoNode.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/SelectorNode.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Parser/Parser.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Parser/TokenStream.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Tests/CssSelectorConverterTest.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Tests/Parser/ParserTest.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Tests/Parser/TokenStreamTest.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/Tests/XPath/TranslatorTest.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Extension/FunctionExtension.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Extension/PseudoClassExtension.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Translator.php
wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/TranslatorInterface.php

index 395e1c8bd76e7bb8c38375bb83fa7f944168b25a..f6316f90ce174d1e58d662f6ba6e2336abd8f059 120000 (symlink)
@@ -1 +1,14 @@
-../leafo/scssphp/bin/pscss
\ No newline at end of file
+#!/usr/bin/env sh
+
+dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../leafo/scssphp/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}/pscss" "$@"
diff --git a/wcfsetup/install/files/lib/system/api/bin/pscss.bat b/wcfsetup/install/files/lib/system/api/bin/pscss.bat
new file mode 100644 (file)
index 0000000..cadaeea
--- /dev/null
@@ -0,0 +1,4 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/../leafo/scssphp/bin/pscss
+php "%BIN_TARGET%" %*
index d5a70ef3d8daf84dddbe2b7fbc56c1f344cacb3b..8170b339600e8e45c594fa28d1c6385d2a8a7d19 100644 (file)
@@ -8,7 +8,7 @@
         }
     },
     "require": {
-        "ezyang/htmlpurifier": "4.10.*",
+        "ezyang/htmlpurifier": "4.12.*",
         "erusev/parsedown": "1.7.*",
         "pelago/emogrifier": "2.1.*",
         "chrisjean/php-ico": "1.0.*",
index a760827465207384158475fd041ca513fdde2d17..9d53f215e63fd1ea2d0d8690bfc4767a30531573 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "b95904d82ddc37f1e3ada56aa0d9637f",
+    "content-hash": "0b588fa9bae7b0d713b4dadff7fbe829",
     "packages": [
         {
             "name": "chrisjean/php-ico",
         },
         {
             "name": "erusev/parsedown",
-            "version": "v1.7.2",
+            "version": "1.7.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/erusev/parsedown.git",
-                "reference": "d60bcdc46978357759ecb13cb4b078da783f8faf"
+                "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/erusev/parsedown/zipball/d60bcdc46978357759ecb13cb4b078da783f8faf",
-                "reference": "d60bcdc46978357759ecb13cb4b078da783f8faf",
+                "url": "https://api.github.com/repos/erusev/parsedown/zipball/6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
+                "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
                 "shasum": ""
             },
             "require": {
                 "markdown",
                 "parser"
             ],
-            "time": "2019-03-17T17:19:46+00:00"
+            "time": "2019-03-17T18:48:37+00:00"
         },
         {
             "name": "ezyang/htmlpurifier",
-            "version": "v4.10.0",
+            "version": "v4.12.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/ezyang/htmlpurifier.git",
-                "reference": "d85d39da4576a6934b72480be6978fb10c860021"
+                "reference": "a617e55bc62a87eec73bd456d146d134ad716f03"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/d85d39da4576a6934b72480be6978fb10c860021",
-                "reference": "d85d39da4576a6934b72480be6978fb10c860021",
+                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/a617e55bc62a87eec73bd456d146d134ad716f03",
+                "reference": "a617e55bc62a87eec73bd456d146d134ad716f03",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.2"
             },
             "require-dev": {
-                "simpletest/simpletest": "^1.1"
+                "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
             },
             "type": "library",
             "autoload": {
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "LGPL"
+                "LGPL-2.1-or-later"
             ],
             "authors": [
                 {
             "keywords": [
                 "html"
             ],
-            "time": "2018-02-23T01:58:20+00:00"
+            "time": "2019-10-28T03:44:26+00:00"
         },
         {
             "name": "leafo/scssphp",
-            "version": "v0.7.7",
+            "version": "v0.7.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/leafo/scssphp.git",
-                "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49"
+                "reference": "a384906af3d078e98b089d7d36f6ceab8703f7ff"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/leafo/scssphp/zipball/1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
-                "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
+                "url": "https://api.github.com/repos/leafo/scssphp/zipball/a384906af3d078e98b089d7d36f6ceab8703f7ff",
+                "reference": "a384906af3d078e98b089d7d36f6ceab8703f7ff",
                 "shasum": ""
             },
             "require": {
                 "scss",
                 "stylesheet"
             ],
-            "time": "2018-07-22T01:22:08+00:00"
+            "time": "2019-04-24T18:10:10+00:00"
         },
         {
             "name": "pear/net_idna2",
         },
         {
             "name": "symfony/css-selector",
-            "version": "v3.4.23",
+            "version": "v3.4.35",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf"
+                "reference": "f819f71ae3ba6f396b4c015bd5895de7d2f1f85f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
-                "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/f819f71ae3ba6f396b4c015bd5895de7d2f1f85f",
+                "reference": "f819f71ae3ba6f396b4c015bd5895de7d2f1f85f",
                 "shasum": ""
             },
             "require": {
                 "MIT"
             ],
             "authors": [
-                {
-                    "name": "Jean-François Simon",
-                    "email": "jeanfrancois.simon@sensiolabs.com"
-                },
                 {
                     "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": "Symfony CssSelector Component",
             "homepage": "https://symfony.com",
-            "time": "2019-01-16T09:39:14+00:00"
+            "time": "2019-10-01T11:57:37+00:00"
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.11.0",
+            "version": "v1.12.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
+                "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
-                "reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
+                "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.11-dev"
+                    "dev-master": "1.12-dev"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2019-02-06T07:57:58+00:00"
+            "time": "2019-08-06T08:03:45+00:00"
         },
         {
             "name": "true/punycode",
index dfe6121e26081b3b3b61355478002873c1e06bf2..0372fce88dc7403c959387c1a474bbb452de88fc 100644 (file)
     },
     {
         "name": "erusev/parsedown",
-        "version": "v1.7.2",
-        "version_normalized": "1.7.2.0",
+        "version": "1.7.3",
+        "version_normalized": "1.7.3.0",
         "source": {
             "type": "git",
             "url": "https://github.com/erusev/parsedown.git",
-            "reference": "d60bcdc46978357759ecb13cb4b078da783f8faf"
+            "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/erusev/parsedown/zipball/d60bcdc46978357759ecb13cb4b078da783f8faf",
-            "reference": "d60bcdc46978357759ecb13cb4b078da783f8faf",
+            "url": "https://api.github.com/repos/erusev/parsedown/zipball/6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
+            "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
             "shasum": ""
         },
         "require": {
@@ -66,7 +66,7 @@
         "require-dev": {
             "phpunit/phpunit": "^4.8.35"
         },
-        "time": "2019-03-17T17:19:46+00:00",
+        "time": "2019-03-17T18:48:37+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
     },
     {
         "name": "ezyang/htmlpurifier",
-        "version": "v4.10.0",
-        "version_normalized": "4.10.0.0",
+        "version": "v4.12.0",
+        "version_normalized": "4.12.0.0",
         "source": {
             "type": "git",
             "url": "https://github.com/ezyang/htmlpurifier.git",
-            "reference": "d85d39da4576a6934b72480be6978fb10c860021"
+            "reference": "a617e55bc62a87eec73bd456d146d134ad716f03"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/d85d39da4576a6934b72480be6978fb10c860021",
-            "reference": "d85d39da4576a6934b72480be6978fb10c860021",
+            "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/a617e55bc62a87eec73bd456d146d134ad716f03",
+            "reference": "a617e55bc62a87eec73bd456d146d134ad716f03",
             "shasum": ""
         },
         "require": {
             "php": ">=5.2"
         },
         "require-dev": {
-            "simpletest/simpletest": "^1.1"
+            "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
         },
-        "time": "2018-02-23T01:58:20+00:00",
+        "time": "2019-10-28T03:44:26+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "LGPL"
+            "LGPL-2.1-or-later"
         ],
         "authors": [
             {
     },
     {
         "name": "leafo/scssphp",
-        "version": "v0.7.7",
-        "version_normalized": "0.7.7.0",
+        "version": "v0.7.8",
+        "version_normalized": "0.7.8.0",
         "source": {
             "type": "git",
             "url": "https://github.com/leafo/scssphp.git",
-            "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49"
+            "reference": "a384906af3d078e98b089d7d36f6ceab8703f7ff"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/leafo/scssphp/zipball/1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
-            "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
+            "url": "https://api.github.com/repos/leafo/scssphp/zipball/a384906af3d078e98b089d7d36f6ceab8703f7ff",
+            "reference": "a384906af3d078e98b089d7d36f6ceab8703f7ff",
             "shasum": ""
         },
         "require": {
             "phpunit/phpunit": "~4.6",
             "squizlabs/php_codesniffer": "~2.5"
         },
-        "time": "2018-07-22T01:22:08+00:00",
+        "time": "2019-04-24T18:10:10+00:00",
         "bin": [
             "bin/pscss"
         ],
     },
     {
         "name": "symfony/css-selector",
-        "version": "v3.4.23",
-        "version_normalized": "3.4.23.0",
+        "version": "v3.4.35",
+        "version_normalized": "3.4.35.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/css-selector.git",
-            "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf"
+            "reference": "f819f71ae3ba6f396b4c015bd5895de7d2f1f85f"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
-            "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
+            "url": "https://api.github.com/repos/symfony/css-selector/zipball/f819f71ae3ba6f396b4c015bd5895de7d2f1f85f",
+            "reference": "f819f71ae3ba6f396b4c015bd5895de7d2f1f85f",
             "shasum": ""
         },
         "require": {
             "php": "^5.5.9|>=7.0.8"
         },
-        "time": "2019-01-16T09:39:14+00:00",
+        "time": "2019-10-01T11:57:37+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
             "MIT"
         ],
         "authors": [
-            {
-                "name": "Jean-François Simon",
-                "email": "jeanfrancois.simon@sensiolabs.com"
-            },
             {
                 "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"
     },
     {
         "name": "symfony/polyfill-mbstring",
-        "version": "v1.11.0",
-        "version_normalized": "1.11.0.0",
+        "version": "v1.12.0",
+        "version_normalized": "1.12.0.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/polyfill-mbstring.git",
-            "reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
+            "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
-            "reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
+            "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
+            "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
             "shasum": ""
         },
         "require": {
         "suggest": {
             "ext-mbstring": "For best performance"
         },
-        "time": "2019-02-06T07:57:58+00:00",
+        "time": "2019-08-06T08:03:45+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.11-dev"
+                "dev-master": "1.12-dev"
             }
         },
         "installation-source": "dist",
index 9b6e10232d682eddc928608e8ff9b847ad1076ab..3528350124cd63e50033dc0a07c792278278616d 100644 (file)
@@ -9,6 +9,40 @@ NEWS ( CHANGELOG and HISTORY )                                     HTMLPurifier
     . Internal change
 ==========================
 
+4.12.0, released 2019-10-27
+! PHP 7.4 is supported, thank you Witold Wasiczko, Mateuz Turcza and
+  Edi Modrić
+- PHPDocs for HTMLModule::addElement() and Bool attr are fixed (thanks
+  Mateusz)
+
+4.11.0, released 2019-07-14
+# SafeScripting now matches case-sensitively against its whitelist (previously it was
+  case-insensitive.)  Thanks Dimitri Gritsajuk <gritsajuk.dimitri@gmail.com>
+  for reporting.
+! New directive %Core.AllowParseManyTags which allows parsing of many nested tags.
+  Thanks M. Suzuki <msuzuki1986@gmail.com> for contributing the patch.
+! purifyArray now supports multidimensional arrays.  Thanks
+  Sandro Miguel Marques <sandromiguel@sandromiguel.com> for contributing this patch.
+! initial and inherit settings available for width, height, and the min-/max-
+  versions thereof.  Thanks Michael Kliewe <info@phpgansta.de> for contributing
+  this patch.
+! More color names are supported.  Thanks Daijobou for contributing.
+- Compatibility fixes for PHP 7.3, including new CI for PHP 7.3
+  (thank you Lukas Neumann <lksnmnn@gmail.com>) and removal of
+  reserved words in our constants (thanks Darko Hrgovic <darko@darkodev.com>
+- Compatibility fixes for HHVM.  Thanks Mateusz Turcza for contributing
+  this fix.
+- HTML Purifier now never defines __autoload, fixing #196.  Thanks
+  Michael Kliewe for reporting.
+- In some situations, Config.php would report an undefined index: class
+  error; this has been fixed.  Thanks DiLong Fa for contributing
+  this fix.
+- We no longer produce <script /> tags; we always explicitly write
+  out the open and close tag.  Thanks Dimitri Gritsajuk
+  <gritsajuk.dimitri@gmail.com> for contributing this fix.
+- Better compatibility when IDNA constants are not present.  Thanks
+  Mateusz Turcza <xemlock@gmail.com> for contributing this fix.
+
 4.10.0, released 2018-02-22
 # PHP 5.3 is no longer officially supported by HTML Purifier
   (we did not specifically break support, but we are no longer
index 37715c607e22b91ca14672bce70d4b7e10ed594c..9e0becc0ae521bb5eaadacd40a2391025f58d45d 100644 (file)
@@ -26,4 +26,4 @@ Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifi
 
 If you're using Composer to manage dependencies, you can use
 
-    $ composer require "ezyang/htmlpurifier":"dev-master"
+    $ composer require ezyang/htmlpurifier
index 1910ba9d233708c993b31c139c45044149f424e5..bcd250ed080fa0b209edb27dfeb581a4c8d2fc9f 100644 (file)
@@ -1 +1 @@
-4.10.0
\ No newline at end of file
+4.12.0
\ No newline at end of file
index 321bdc5661f787912f8f4bcdfb0aed18e033403e..3158b2b86ff281422bc6a34e673cf31d642585cd 100644 (file)
@@ -7,7 +7,7 @@
  * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
  * FILE, changes will be overwritten the next time the script is run.
  *
- * @version 4.10.0
+ * @version 4.12.0
  *
  * @warning
  *      You must *not* include any other HTML Purifier files before this file,
index bada5188b48dc370c64ac25a44e4c42ab3387840..58bbddb0259c1fa8ff3187ff629419050037d46c 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 /*
-    HTML Purifier 4.10.0 - Standards Compliant HTML Filtering
+    HTML Purifier 4.12.0 - Standards Compliant HTML Filtering
     Copyright (C) 2006-2008 Edward Z. Yang
 
     This library is free software; you can redistribute it and/or
@@ -58,12 +58,12 @@ class HTMLPurifier
      * Version of HTML Purifier.
      * @type string
      */
-    public $version = '4.10.0';
+    public $version = '4.12.0';
 
     /**
      * Constant with version of HTML Purifier.
      */
-    const VERSION = '4.10.0';
+    const VERSION = '4.12.0';
 
     /**
      * Global configuration object.
@@ -240,12 +240,16 @@ class HTMLPurifier
     public function purifyArray($array_of_html, $config = null)
     {
         $context_array = array();
-        foreach ($array_of_html as $key => $html) {
-            $array_of_html[$key] = $this->purify($html, $config);
+        foreach($array_of_html as $key=>$value){
+            if (is_array($value)) {
+                $array[$key] = $this->purifyArray($value, $config);
+            } else {
+                $array[$key] = $this->purify($value, $config);
+            }
             $context_array[$key] = $this->context;
         }
         $this->context = $context_array;
-        return $array_of_html;
+        return $array;
     }
 
     /**
index e54a3344a74ffdff81562babdd7a898d4f05e836..1beeaa5d22e3bc352b5932c23966b0a8a690688d 100644 (file)
@@ -97,7 +97,11 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
 
         // PHP 5.3 and later support this functionality natively
         if (function_exists('idn_to_ascii')) {
-            $string = idn_to_ascii($string, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+            if (defined('IDNA_NONTRANSITIONAL_TO_ASCII') && defined('INTL_IDNA_VARIANT_UTS46')) {
+                $string = idn_to_ascii($string, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+            } else {
+                $string = idn_to_ascii($string);
+            }
 
         // If we have Net_IDNA2 support, we can support IRIs by
         // punycoding them. (This is the most portable thing to do,
index f37cf3713de72732ab70ae68d95b82bd8d3fd680..f569d40c9e12d4a1c60d828b3cc5d224ed3d12bd 100644 (file)
@@ -21,7 +21,7 @@ class HTMLPurifier_Config
      * HTML Purifier's version
      * @type string
      */
-    public $version = '4.10.0';
+    public $version = '4.12.0';
 
     /**
      * Whether or not to automatically finalize
@@ -890,7 +890,7 @@ class HTMLPurifier_Config
             // zip(tail(trace), trace) -- but PHP is not Haskell har har
             for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
                 // XXX this is not correct on some versions of HTML Purifier
-                if ($trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
+                if (isset($trace[$i + 1]['class']) && $trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
                     continue;
                 }
                 $frame = $trace[$i];
index 6238a99e34e59cb38d4e61eebdc3517dae101066..ca5f25b849fdbe3c61eea8da0bb6792cb5dff83e 100644 (file)
@@ -68,8 +68,18 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
         $doc = new DOMDocument();
         $doc->encoding = 'UTF-8'; // theoretically, the above has this covered
 
+        $options = 0;
+        if ($config->get('Core.AllowParseManyTags') && defined('LIBXML_PARSEHUGE')) {
+            $options |= LIBXML_PARSEHUGE;
+        }
+
         set_error_handler(array($this, 'muteErrorHandler'));
-        $doc->loadHTML($html);
+        // loadHTML() fails on PHP 5.3 when second parameter is given
+        if ($options) {
+            $doc->loadHTML($html, $options);
+        } else {
+            $doc->loadHTML($html);
+        }
         restore_error_handler();
 
         $body = $doc->getElementsByTagName('html')->item(0)-> // <html>
@@ -133,11 +143,11 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
      */
     protected function getTagName($node)
     {
-        if (property_exists($node, 'tagName')) {
+        if (isset($node->tagName)) {
             return $node->tagName;
-        } else if (property_exists($node, 'nodeName')) {
+        } else if (isset($node->nodeName)) {
             return $node->nodeName;
-        } else if (property_exists($node, 'localName')) {
+        } else if (isset($node->localName)) {
             return $node->localName;
         }
         return null;
@@ -150,11 +160,11 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
      */
     protected function getData($node)
     {
-        if (property_exists($node, 'data')) {
+        if (isset($node->data)) {
             return $node->data;
-        } else if (property_exists($node, 'nodeValue')) {
+        } else if (isset($node->nodeValue)) {
             return $node->nodeValue;
-        } else if (property_exists($node, 'textContent')) {
+        } else if (isset($node->textContent)) {
             return $node->textContent;
         }
         return null;
index be1cb51cbabe1f4e712783429f4bd979083372a7..ce80e6a992711ad4ecb062a0f130fdc176890c24 100644 (file)
@@ -201,7 +201,7 @@ if ($style) {
 }
 
 if ($sourceMap) {
-    $scss->setSourceMap(Compiler::SOURCE_MAP_FILE);
+    $scss->setSourceMap(Compiler::SOURCE_MAP_INLINE);
 }
 
 if ($encoding) {
index 6ea970185f2888320a47129bb685263ebb1d43a5..54060b1a2be2875d626da8c16d2c3f0bad7bb2d7 100644 (file)
@@ -468,7 +468,7 @@ class Server
 
         if ($root !== null) {
             // If we have a root value which means we should rebuild.
-            $out = array();
+            $out = [];
             $out['root'] = $root;
             $out['compiled'] = $this->compileFile($root);
             $out['files'] = $this->scss->getParsedFiles();
index 13c84bf5d6e23a602d1b06de5e8864cf08801ce0..d75ecb55ad220357b70c3b11ca2cf6986743330d 100644 (file)
@@ -24,7 +24,8 @@ if (! class_exists('Leafo\ScssPhp\Version', false)) {
     include_once __DIR__ . '/src/Node.php';
     include_once __DIR__ . '/src/Node/Number.php';
     include_once __DIR__ . '/src/Parser.php';
-    include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php';
+    include_once __DIR__ . '/src/SourceMap/Base64.php';
+    include_once __DIR__ . '/src/SourceMap/Base64VLQ.php';
     include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php';
     include_once __DIR__ . '/src/Type.php';
     include_once __DIR__ . '/src/Util.php';
index a6ef8e034f3a5aa1e8e85d0a1bb6eec7cb845b26..2edbc8a7d6bee08e8d86e4c4f331131d59e6fbc5 100644 (file)
@@ -62,4 +62,8 @@ class Block
      * @var array
      */
     public $children;
+    /**
+     * @var \Leafo\ScssPhp\Block
+     */
+    public $selfParent;
 }
index 5003be8f7920ee649c7d080f14cf79f5b0c8af3b..87fb8b25872dec5a3c47281a30a760f79b406395 100644 (file)
@@ -145,18 +145,18 @@ class Compiler
     protected $charsetSeen;
     protected $sourceNames;
 
-    private $indentLevel;
-    private $commentsSeen;
-    private $extends;
-    private $extendsMap;
-    private $parsedFiles;
-    private $parser;
-    private $sourceIndex;
-    private $sourceLine;
-    private $sourceColumn;
-    private $stderr;
-    private $shouldEvaluate;
-    private $ignoreErrors;
+    protected $indentLevel;
+    protected $commentsSeen;
+    protected $extends;
+    protected $extendsMap;
+    protected $parsedFiles;
+    protected $parser;
+    protected $sourceIndex;
+    protected $sourceLine;
+    protected $sourceColumn;
+    protected $stderr;
+    protected $shouldEvaluate;
+    protected $ignoreErrors;
 
     /**
      * Constructor
@@ -463,7 +463,12 @@ class Compiler
                     $tempReplacement = $k > 0 ? array_slice($new, $k) : $new;
 
                     for ($l = count($tempReplacement) - 1; $l >= 0; $l--) {
-                        $slice = $tempReplacement[$l];
+                        $slice = [];
+                        foreach ($tempReplacement[$l] as $chunk) {
+                            if (!in_array($chunk, $slice)) {
+                                $slice[] = $chunk;
+                            }
+                        }
                         array_unshift($replacement, $slice);
 
                         if (! $this->isImmediateRelationshipCombinator(end($slice))) {
@@ -790,6 +795,7 @@ class Compiler
             $wrapped->comments     = [];
             $wrapped->parent       = $block;
             $wrapped->children     = $block->children;
+            $wrapped->selfParent   = $block->selfParent;
 
             $block->children = [[Type::T_BLOCK, $wrapped]];
         }
@@ -817,7 +823,7 @@ class Compiler
      *
      * @return array
      */
-    private function spliceTree($envs, Block $block, $without)
+    protected function spliceTree($envs, Block $block, $without)
     {
         $newBlock = null;
 
@@ -882,6 +888,7 @@ class Compiler
             $newBlock = $b;
         }
 
+        $newBlock->selfParent   = $block->selfParent;
         $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK;
 
         return [$type, $newBlock];
@@ -894,7 +901,7 @@ class Compiler
      *
      * @return integer
      */
-    private function compileWith($with)
+    protected function compileWith($with)
     {
         static $mapping = [
             'rule'     => self::WITH_RULE,
@@ -945,7 +952,7 @@ class Compiler
      *
      * @return \Leafo\ScssPhp\Compiler\Environment
      */
-    private function filterWithout($envs, $without)
+    protected function filterWithout($envs, $without)
     {
         $filtered = [];
 
@@ -968,7 +975,7 @@ class Compiler
      *
      * @return boolean
      */
-    private function isWithout($without, Block $block)
+    protected function isWithout($without, Block $block)
     {
         if ((($without & static::WITH_RULE) && isset($block->selectors)) ||
             (($without & static::WITH_MEDIA) &&
@@ -1083,7 +1090,7 @@ class Compiler
         $this->scope->children[] = $out;
 
         if (count($block->children)) {
-            $out->selectors = $this->multiplySelectors($env);
+            $out->selectors = $this->multiplySelectors($env, $block->selfParent);
 
             $this->compileChildrenNoReturn($block->children, $out);
         }
@@ -1308,7 +1315,7 @@ class Compiler
      * @param array                                $stms
      * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
      *
-     * @return array
+     * @return array|null
      */
     protected function compileChildren($stms, OutputBlock $out)
     {
@@ -1319,6 +1326,7 @@ class Compiler
                 return $ret;
             }
         }
+        return null;
     }
 
     /**
@@ -1575,9 +1583,7 @@ class Compiler
 
         switch ($child[0]) {
             case Type::T_SCSSPHP_IMPORT_ONCE:
-                list(, $rawPath) = $child;
-
-                $rawPath = $this->reduce($rawPath);
+                $rawPath = $this->reduce($child[1]);
 
                 if (! $this->compileImport($rawPath, $out, true)) {
                     $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';';
@@ -1585,9 +1591,7 @@ class Compiler
                 break;
 
             case Type::T_IMPORT:
-                list(, $rawPath) = $child;
-
-                $rawPath = $this->reduce($rawPath);
+                $rawPath = $this->reduce($child[1]);
 
                 if (! $this->compileImport($rawPath, $out)) {
                     $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';';
@@ -1627,7 +1631,7 @@ class Compiler
                     $isGlobal = in_array('!global', $flags);
 
                     if ($isGlobal) {
-                        $this->set($name[1], $this->reduce($value), false, $this->rootEnv);
+                        $this->set($name[1], $this->reduce($value), false, $this->rootEnv, $value);
                         break;
                     }
 
@@ -1636,7 +1640,7 @@ class Compiler
                         || $result === static::$null);
 
                     if (! $isDefault || $shouldSet) {
-                        $this->set($name[1], $this->reduce($value));
+                        $this->set($name[1], $this->reduce($value), false, null, $value);
                     }
                     break;
                 }
@@ -1645,10 +1649,23 @@ class Compiler
 
                 // handle shorthand syntax: size / line-height
                 if ($compiledName === 'font') {
-                    if ($value[0] === Type::T_EXPRESSION && $value[1] === '/') {
-                        $value = $this->expToString($value);
-                    } elseif ($value[0] === Type::T_LIST) {
-                        foreach ($value[2] as &$item) {
+                    if ($value[0] === Type::T_VARIABLE) {
+                        // if the font value comes from variable, the content is already reduced (which means formulars where already calculated)
+                        // so we need the original unreduced value
+                        $value = $this->get($value[1], true, null, true);
+                    }
+
+                    $fontValue=&$value;
+                    if ($value[0] === Type::T_LIST && $value[1]==',') {
+                        // this is the case if more than one font is given: example: "font: 400 1em/1.3 arial,helvetica"
+                        // we need to handle the first list element
+                        $fontValue=&$value[2][0];
+                    }
+
+                    if ($fontValue[0] === Type::T_EXPRESSION && $fontValue[1] === '/') {
+                        $fontValue = $this->expToString($fontValue);
+                    } elseif ($fontValue[0] === Type::T_LIST) {
+                        foreach ($fontValue[2] as &$item) {
                             if ($item[0] === Type::T_EXPRESSION && $item[1] === '/') {
                                 $item = $this->expToString($item);
                             }
@@ -1691,9 +1708,7 @@ class Compiler
                 break;
 
             case Type::T_EXTEND:
-                list(, $selectors) = $child;
-
-                foreach ($selectors as $sel) {
+                foreach ($child[1] as $sel) {
                     $results = $this->evalSelectors([$sel]);
 
                     foreach ($results as $result) {
@@ -1910,25 +1925,28 @@ class Compiler
             case Type::T_DEBUG:
                 list(, $value) = $child;
 
+                $fname = $this->sourceNames[$this->sourceIndex];
                 $line = $this->sourceLine;
                 $value = $this->compileValue($this->reduce($value, true));
-                fwrite($this->stderr, "Line $line DEBUG: $value\n");
+                fwrite($this->stderr, "File $fname on line $line DEBUG: $value\n");
                 break;
 
             case Type::T_WARN:
                 list(, $value) = $child;
 
+                $fname = $this->sourceNames[$this->sourceIndex];
                 $line = $this->sourceLine;
                 $value = $this->compileValue($this->reduce($value, true));
-                fwrite($this->stderr, "Line $line WARN: $value\n");
+                fwrite($this->stderr, "File $fname on line $line WARN: $value\n");
                 break;
 
             case Type::T_ERROR:
                 list(, $value) = $child;
 
+                $fname = $this->sourceNames[$this->sourceIndex];
                 $line = $this->sourceLine;
                 $value = $this->compileValue($this->reduce($value, true));
-                $this->throwError("Line $line ERROR: $value\n");
+                $this->throwError("File $fname on line $line ERROR: $value\n");
                 break;
 
             case Type::T_CONTROL:
@@ -2004,7 +2022,7 @@ class Compiler
         switch ($value[0]) {
             case Type::T_EXPRESSION:
                 if ($value[1] === '/') {
-                    return $this->shouldEval($value[2]$value[3]);
+                    return $this->shouldEval($value[2]) || $this->shouldEval($value[3]);
                 }
 
                 // fall-thru
@@ -2026,9 +2044,8 @@ class Compiler
      */
     protected function reduce($value, $inExp = false)
     {
-        list($type) = $value;
 
-        switch ($type) {
+        switch ($value[0]) {
             case Type::T_EXPRESSION:
                 list(, $op, $left, $right, $inParens) = $value;
 
@@ -2161,9 +2178,7 @@ class Compiler
                 return [Type::T_STRING, '', [$op, $exp]];
 
             case Type::T_VARIABLE:
-                list(, $name) = $value;
-
-                return $this->reduce($this->get($name));
+                return $this->reduce($this->get($value[1]));
 
             case Type::T_LIST:
                 foreach ($value[2] as &$item) {
@@ -2198,9 +2213,7 @@ class Compiler
                 return $value;
 
             case Type::T_FUNCTION_CALL:
-                list(, $name, $argValues) = $value;
-
-                return $this->fncall($name, $argValues);
+                return $this->fncall($value[1], $value[2]);
 
             default:
                 return $value;
@@ -2215,7 +2228,7 @@ class Compiler
      *
      * @return array|null
      */
-    private function fncall($name, $argValues)
+    protected function fncall($name, $argValues)
     {
         // SCSS @function
         if ($this->callScssFunction($name, $argValues, $returnValue)) {
@@ -2261,9 +2274,8 @@ class Compiler
     public function normalizeValue($value)
     {
         $value = $this->coerceForExpression($this->reduce($value));
-        list($type) = $value;
 
-        switch ($type) {
+        switch ($value[0]) {
             case Type::T_LIST:
                 $value = $this->extractInterpolation($value);
 
@@ -2278,7 +2290,7 @@ class Compiler
                 return $value;
 
             case Type::T_STRING:
-                return [$type, '"', [$this->compileStringContent($value)]];
+                return [$value[0], '"', [$this->compileStringContent($value)]];
 
             case Type::T_NUMBER:
                 return $value->normalize();
@@ -2366,7 +2378,7 @@ class Compiler
      * @param array $left
      * @param array $right
      *
-     * @return array
+     * @return array|null
      */
     protected function opAdd($left, $right)
     {
@@ -2389,6 +2401,7 @@ class Compiler
 
             return $strRight;
         }
+        return null;
     }
 
     /**
@@ -2398,12 +2411,12 @@ class Compiler
      * @param array   $right
      * @param boolean $shouldEval
      *
-     * @return array
+     * @return array|null
      */
     protected function opAnd($left, $right, $shouldEval)
     {
         if (! $shouldEval) {
-            return;
+            return null;
         }
 
         if ($left !== static::$false and $left !== static::$null) {
@@ -2420,12 +2433,12 @@ class Compiler
      * @param array   $right
      * @param boolean $shouldEval
      *
-     * @return array
+     * @return array|null
      */
     protected function opOr($left, $right, $shouldEval)
     {
         if (! $shouldEval) {
-            return;
+            return null;
         }
 
         if ($left !== static::$false and $left !== static::$null) {
@@ -2683,9 +2696,7 @@ class Compiler
     {
         $value = $this->reduce($value);
 
-        list($type) = $value;
-
-        switch ($type) {
+        switch ($value[0]) {
             case Type::T_KEYWORD:
                 return $value[1];
 
@@ -2780,11 +2791,8 @@ class Compiler
                 return $left . $this->compileValue($interpolate) . $right;
 
             case Type::T_INTERPOLATE:
-                // raw parse node
-                list(, $exp) = $value;
-
                 // strip quotes if it's a string
-                $reduced = $this->reduce($exp);
+                $reduced = $this->reduce($value[1]);
 
                 switch ($reduced[0]) {
                     case Type::T_LIST:
@@ -2834,7 +2842,7 @@ class Compiler
                 return 'null';
 
             default:
-                $this->throwError("unknown value type: $type");
+                $this->throwError("unknown value type: $value[0]");
         }
     }
 
@@ -2899,15 +2907,21 @@ class Compiler
      * Find the final set of selectors
      *
      * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param Leafo\ScssPhp\Block $selfParent
      *
      * @return array
      */
-    protected function multiplySelectors(Environment $env)
+    protected function multiplySelectors(Environment $env, $selfParent = null)
     {
         $envs            = $this->compactEnv($env);
         $selectors       = [];
         $parentSelectors = [[]];
 
+        $selfParentSelectors = null;
+        if (!is_null($selfParent) and $selfParent->selectors) {
+            $selfParentSelectors = $this->evalSelectors($selfParent->selectors);
+        }
+
         while ($env = array_pop($envs)) {
             if (empty($env->selectors)) {
                 continue;
@@ -2917,7 +2931,7 @@ class Compiler
 
             foreach ($env->selectors as $selector) {
                 foreach ($parentSelectors as $parent) {
-                    $selectors[] = $this->joinSelectors($parent, $selector);
+                    $selectors[] = $this->joinSelectors($parent, $selector, $selfParentSelectors);
                 }
             }
 
@@ -2932,10 +2946,10 @@ class Compiler
      *
      * @param array $parent
      * @param array $child
-     *
+     * @param array $selfParentSelectors
      * @return array
      */
-    protected function joinSelectors($parent, $child)
+    protected function joinSelectors($parent, $child, $selfParentSelectors = null)
     {
         $setSelf = false;
         $out = [];
@@ -2946,15 +2960,17 @@ class Compiler
             foreach ($part as $p) {
                 if ($p === static::$selfSelector) {
                     $setSelf = true;
-
-                    foreach ($parent as $i => $parentPart) {
+                    if (is_null($selfParentSelectors)) {
+                        $selfParentSelectors = $parent;
+                    }
+                    foreach ($selfParentSelectors as $i => $parentPart) {
                         if ($i > 0) {
                             $out[] = $newPart;
                             $newPart = [];
                         }
 
                         foreach ($parentPart as $pp) {
-                            $newPart[] = $pp;
+                            $newPart[] = (is_array($pp) ? implode($pp) : $pp);
                         }
                     }
                 } else {
@@ -3016,7 +3032,7 @@ class Compiler
      *
      * @return array
      */
-    private function compactEnv(Environment $env)
+    protected function compactEnv(Environment $env)
     {
         for ($envs = []; $env; $env = $env->parent) {
             $envs[] = $env;
@@ -3032,7 +3048,7 @@ class Compiler
      *
      * @return \Leafo\ScssPhp\Compiler\Environment
      */
-    private function extractEnv($envs)
+    protected function extractEnv($envs)
     {
         for ($env = null; $e = array_pop($envs);) {
             $e->parent = $env;
@@ -3087,8 +3103,9 @@ class Compiler
      * @param mixed                               $value
      * @param boolean                             $shadow
      * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param mixed                               $valueUnreduced
      */
-    protected function set($name, $value, $shadow = false, Environment $env = null)
+    protected function set($name, $value, $shadow = false, Environment $env = null, $valueUnreduced = null)
     {
         $name = $this->normalizeName($name);
 
@@ -3097,9 +3114,9 @@ class Compiler
         }
 
         if ($shadow) {
-            $this->setRaw($name, $value, $env);
+            $this->setRaw($name, $value, $env, $valueUnreduced);
         } else {
-            $this->setExisting($name, $value, $env);
+            $this->setExisting($name, $value, $env, $valueUnreduced);
         }
     }
 
@@ -3109,8 +3126,9 @@ class Compiler
      * @param string                              $name
      * @param mixed                               $value
      * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param mixed                               $valueUnreduced
      */
-    protected function setExisting($name, $value, Environment $env)
+    protected function setExisting($name, $value, Environment $env, $valueUnreduced = null)
     {
         $storeEnv = $env;
 
@@ -3135,6 +3153,9 @@ class Compiler
         }
 
         $env->store[$name] = $value;
+        if ($valueUnreduced) {
+            $env->storeUnreduced[$name] = $valueUnreduced;
+        }
     }
 
     /**
@@ -3143,10 +3164,14 @@ class Compiler
      * @param string                              $name
      * @param mixed                               $value
      * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param mixed                               $valueUnreduced
      */
-    protected function setRaw($name, $value, Environment $env)
+    protected function setRaw($name, $value, Environment $env, $valueUnreduced = null)
     {
         $env->store[$name] = $value;
+        if ($valueUnreduced) {
+            $env->storeUnreduced[$name] = $valueUnreduced;
+        }
     }
 
     /**
@@ -3157,10 +3182,11 @@ class Compiler
      * @param string                              $name
      * @param boolean                             $shouldThrow
      * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param boolean                             $unreduced
      *
-     * @return mixed
+     * @return mixed|null
      */
-    public function get($name, $shouldThrow = true, Environment $env = null)
+    public function get($name, $shouldThrow = true, Environment $env = null, $unreduced = false)
     {
         $normalizedName = $this->normalizeName($name);
         $specialContentKey = static::$namespaces['special'] . 'content';
@@ -3174,6 +3200,9 @@ class Compiler
 
         for (;;) {
             if (array_key_exists($normalizedName, $env->store)) {
+                if ($unreduced && isset($env->storeUnreduced[$normalizedName])) {
+                    return $env->storeUnreduced[$normalizedName];
+                }
                 return $env->store[$normalizedName];
             }
 
@@ -3200,6 +3229,7 @@ class Compiler
         }
 
         // found nothing
+        return null;
     }
 
     /**
@@ -3308,7 +3338,7 @@ class Compiler
      *
      * @api
      *
-     * @param string $path
+     * @param string|callable $path
      */
     public function addImportPath($path)
     {
@@ -3481,9 +3511,12 @@ class Compiler
             if (is_string($dir)) {
                 // check urls for normal import paths
                 foreach ($urls as $full) {
-                    $full = $dir
-                        . (! empty($dir) && substr($dir, -1) !== '/' ? '/' : '')
-                        . $full;
+                    $separator = (
+                        !empty($dir) &&
+                        substr($dir, -1) !== '/' &&
+                        substr($full, 0, 1) !== '/'
+                    ) ? '/' : '';
+                    $full = $dir . $separator . $full;
 
                     if ($this->fileExists($file = $full . '.scss') ||
                         ($hasExtension && $this->fileExists($file = $full))
@@ -3528,6 +3561,7 @@ class Compiler
     public function setIgnoreErrors($ignoreErrors)
     {
         $this->ignoreErrors = $ignoreErrors;
+        return $this;
     }
 
     /**
@@ -3550,7 +3584,8 @@ class Compiler
         }
 
         $line = $this->sourceLine;
-        $msg = "$msg: line: $line";
+        $loc = isset($this->sourceNames[$this->sourceIndex]) ? $this->sourceNames[$this->sourceIndex] . " on line $line" : "line: $line";
+        $msg = "$msg: $loc";
 
         throw new CompilerException($msg);
     }
@@ -3711,9 +3746,7 @@ class Compiler
         foreach ($args as $arg) {
             list($key, $value) = $arg;
 
-            if ($key !== null) {
-                $key = $key[1];
-            }
+            $key = $key[1];
 
             if (empty($key)) {
                 $posArgs[] = empty($arg[2]) ? $value : $arg;
@@ -3866,7 +3899,7 @@ class Compiler
      *
      * @return array|\Leafo\ScssPhp\Node\Number
      */
-    private function coerceValue($value)
+    protected function coerceValue($value)
     {
         if (is_array($value) || $value instanceof \ArrayAccess) {
             return $value;
@@ -4068,7 +4101,7 @@ class Compiler
         $value = $this->coerceMap($value);
 
         if ($value[0] !== Type::T_MAP) {
-            $this->throwError('expecting map');
+            $this->throwError('expecting map, %s received', $value[0]);
         }
 
         return $value;
@@ -4088,7 +4121,7 @@ class Compiler
     public function assertList($value)
     {
         if ($value[0] !== Type::T_LIST) {
-            $this->throwError('expecting list');
+            $this->throwError('expecting list, %s received', $value[0]);
         }
 
         return $value;
@@ -4111,7 +4144,7 @@ class Compiler
             return $color;
         }
 
-        $this->throwError('expecting color');
+        $this->throwError('expecting color, %s received', $value[0]);
     }
 
     /**
@@ -4128,7 +4161,7 @@ class Compiler
     public function assertNumber($value)
     {
         if ($value[0] !== Type::T_NUMBER) {
-            $this->throwError('expecting number');
+            $this->throwError('expecting number, %s received', $value[0]);
         }
 
         return $value[1];
@@ -4205,7 +4238,7 @@ class Compiler
      *
      * @return float
      */
-    private function hueToRGB($m1, $m2, $h)
+    protected function hueToRGB($m1, $m2, $h)
     {
         if ($h < 0) {
             $h += 1;
@@ -4927,7 +4960,7 @@ class Compiler
         if (! isset($list[2][$n])) {
             $this->throwError('Invalid argument for "n"');
 
-            return;
+            return null;
         }
 
         $list[2][$n] = $args[2];
@@ -5165,7 +5198,7 @@ class Compiler
         ) {
             $this->throwError('Invalid argument(s) for "comparable"');
 
-            return;
+            return null;
         }
 
         $number1 = $number1->normalize();
@@ -5345,7 +5378,7 @@ class Compiler
             if ($n < 1) {
                 $this->throwError("limit must be greater than or equal to 1");
 
-                return;
+                return null;
             }
 
             return new Node\Number(mt_rand(1, $n), '');
index fe309dd30f86f4cae756eb490f029c3db0683249..99231f3682a8f3df737333ef4cb3edda404ceb2e 100644 (file)
@@ -33,6 +33,11 @@ class Environment
      */
     public $store;
 
+    /**
+     * @var array
+     */
+    public $storeUnreduced;
+
     /**
      * @var integer
      */
index de86f094fb6d89d6b3829fa8c0e14454aedac513..1eda928f00610230072ed7cc47bfbe1205456908 100644 (file)
@@ -271,7 +271,7 @@ class Parser
      * the buffer position will be left at an invalid state. In order to
      * avoid this, Compiler::seek() is used to remember and set buffer positions.
      *
-     * Before parsing a chain, use $s = $this->seek() to remember the current
+     * Before parsing a chain, use $s = $this->count to remember the current
      * position into $s. Then if a chain fails, use $this->seek($s) to
      * go back where we started.
      *
@@ -279,14 +279,14 @@ class Parser
      */
     protected function parseChunk()
     {
-        $s = $this->seek();
+        $s = $this->count;
 
         // the directives
         if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') {
-            if ($this->literal('@at-root') &&
+            if ($this->literal('@at-root', 8) &&
                 ($this->selectors($selector) || true) &&
                 ($this->map($with) || true) &&
-                $this->literal('{')
+                $this->matchChar('{')
             ) {
                 $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
                 $atRoot->selector = $selector;
@@ -297,7 +297,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) {
+            if ($this->literal('@media', 6) && $this->mediaQueryList($mediaQueryList) && $this->matchChar('{')) {
                 $media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
                 $media->queryList = $mediaQueryList[2];
 
@@ -306,10 +306,10 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@mixin') &&
+            if ($this->literal('@mixin', 6) &&
                 $this->keyword($mixinName) &&
                 ($this->argumentDef($args) || true) &&
-                $this->literal('{')
+                $this->matchChar('{')
             ) {
                 $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
                 $mixin->name = $mixinName;
@@ -320,13 +320,13 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@include') &&
+            if ($this->literal('@include', 8) &&
                 $this->keyword($mixinName) &&
-                ($this->literal('(') &&
+                ($this->matchChar('(') &&
                     ($this->argValues($argValues) || true) &&
-                    $this->literal(')') || true) &&
+                    $this->matchChar(')') || true) &&
                 ($this->end() ||
-                    $this->literal('{') && $hasBlock = true)
+                    $this->matchChar('{') && $hasBlock = true)
             ) {
                 $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null];
 
@@ -342,7 +342,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@scssphp-import-once') &&
+            if ($this->literal('@scssphp-import-once', 20) &&
                 $this->valueList($importPath) &&
                 $this->end()
             ) {
@@ -353,7 +353,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@import') &&
+            if ($this->literal('@import', 7) &&
                 $this->valueList($importPath) &&
                 $this->end()
             ) {
@@ -364,7 +364,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@import') &&
+            if ($this->literal('@import', 7) &&
                 $this->url($importPath) &&
                 $this->end()
             ) {
@@ -375,7 +375,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@extend') &&
+            if ($this->literal('@extend', 7) &&
                 $this->selectors($selectors) &&
                 $this->end()
             ) {
@@ -388,10 +388,10 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@function') &&
+            if ($this->literal('@function', 9) &&
                 $this->keyword($fnName) &&
                 $this->argumentDef($args) &&
-                $this->literal('{')
+                $this->matchChar('{')
             ) {
                 $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
                 $func->name = $fnName;
@@ -402,7 +402,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@break') && $this->end()) {
+            if ($this->literal('@break', 6) && $this->end()) {
                 $this->append([Type::T_BREAK], $s);
 
                 return true;
@@ -410,7 +410,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@continue') && $this->end()) {
+            if ($this->literal('@continue', 9) && $this->end()) {
                 $this->append([Type::T_CONTINUE], $s);
 
                 return true;
@@ -419,7 +419,7 @@ class Parser
             $this->seek($s);
 
 
-            if ($this->literal('@return') && ($this->valueList($retVal) || true) && $this->end()) {
+            if ($this->literal('@return', 7) && ($this->valueList($retVal) || true) && $this->end()) {
                 $this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s);
 
                 return true;
@@ -427,11 +427,11 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@each') &&
+            if ($this->literal('@each', 5) &&
                 $this->genericList($varNames, 'variable', ',', false) &&
-                $this->literal('in') &&
+                $this->literal('in', 2) &&
                 $this->valueList($list) &&
-                $this->literal('{')
+                $this->matchChar('{')
             ) {
                 $each = $this->pushSpecialBlock(Type::T_EACH, $s);
 
@@ -446,9 +446,9 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@while') &&
+            if ($this->literal('@while', 6) &&
                 $this->expression($cond) &&
-                $this->literal('{')
+                $this->matchChar('{')
             ) {
                 $while = $this->pushSpecialBlock(Type::T_WHILE, $s);
                 $while->cond = $cond;
@@ -458,14 +458,14 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@for') &&
+            if ($this->literal('@for', 4) &&
                 $this->variable($varName) &&
-                $this->literal('from') &&
+                $this->literal('from', 4) &&
                 $this->expression($start) &&
-                ($this->literal('through') ||
-                    ($forUntil = true && $this->literal('to'))) &&
+                ($this->literal('through', 7) ||
+                    ($forUntil = true && $this->literal('to', 2))) &&
                 $this->expression($end) &&
-                $this->literal('{')
+                $this->matchChar('{')
             ) {
                 $for = $this->pushSpecialBlock(Type::T_FOR, $s);
                 $for->var = $varName[1];
@@ -478,7 +478,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) {
+            if ($this->literal('@if', 3) && $this->valueList($cond) && $this->matchChar('{')) {
                 $if = $this->pushSpecialBlock(Type::T_IF, $s);
                 $if->cond = $cond;
                 $if->cases = [];
@@ -488,7 +488,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@debug') &&
+            if ($this->literal('@debug', 6) &&
                 $this->valueList($value) &&
                 $this->end()
             ) {
@@ -499,7 +499,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@warn') &&
+            if ($this->literal('@warn', 5) &&
                 $this->valueList($value) &&
                 $this->end()
             ) {
@@ -510,7 +510,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@error') &&
+            if ($this->literal('@error', 6) &&
                 $this->valueList($value) &&
                 $this->end()
             ) {
@@ -521,7 +521,7 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@content') && $this->end()) {
+            if ($this->literal('@content', 8) && $this->end()) {
                 $this->append([Type::T_MIXIN_CONTENT], $s);
 
                 return true;
@@ -534,10 +534,10 @@ class Parser
             if (isset($last) && $last[0] === Type::T_IF) {
                 list(, $if) = $last;
 
-                if ($this->literal('@else')) {
-                    if ($this->literal('{')) {
+                if ($this->literal('@else', 5)) {
+                    if ($this->matchChar('{')) {
                         $else = $this->pushSpecialBlock(Type::T_ELSE, $s);
-                    } elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) {
+                    } elseif ($this->literal('if', 2) && $this->valueList($cond) && $this->matchChar('{')) {
                         $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s);
                         $else->cond = $cond;
                     }
@@ -554,7 +554,7 @@ class Parser
             }
 
             // only retain the first @charset directive encountered
-            if ($this->literal('@charset') &&
+            if ($this->literal('@charset', 8) &&
                 $this->valueList($charset) &&
                 $this->end()
             ) {
@@ -576,10 +576,10 @@ class Parser
             $this->seek($s);
 
             // doesn't match built in directive, do generic one
-            if ($this->literal('@', false) &&
+            if ($this->matchChar('@', false) &&
                 $this->keyword($dirName) &&
                 ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) &&
-                $this->literal('{')
+                $this->matchChar('{')
             ) {
                 if ($dirName === 'media') {
                     $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s);
@@ -603,7 +603,7 @@ class Parser
         // property shortcut
         // captures most properties before having to parse a selector
         if ($this->keyword($name, false) &&
-            $this->literal(': ') &&
+            $this->literal(': ', 2) &&
             $this->valueList($value) &&
             $this->end()
         ) {
@@ -617,7 +617,7 @@ class Parser
 
         // variable assigns
         if ($this->variable($name) &&
-            $this->literal(':') &&
+            $this->matchChar(':') &&
             $this->valueList($value) &&
             $this->end()
         ) {
@@ -631,12 +631,12 @@ class Parser
         $this->seek($s);
 
         // misc
-        if ($this->literal('-->')) {
+        if ($this->literal('-->', 3)) {
             return true;
         }
 
         // opening css block
-        if ($this->selectors($selectors) && $this->literal('{')) {
+        if ($this->selectors($selectors) && $this->matchChar('{')) {
             $this->pushBlock($selectors, $s);
 
             return true;
@@ -645,7 +645,7 @@ class Parser
         $this->seek($s);
 
         // property assign, or nested assign
-        if ($this->propertyName($name) && $this->literal(':')) {
+        if ($this->propertyName($name) && $this->matchChar(':')) {
             $foundSomething = false;
 
             if ($this->valueList($value)) {
@@ -653,7 +653,7 @@ class Parser
                 $foundSomething = true;
             }
 
-            if ($this->literal('{')) {
+            if ($this->matchChar('{')) {
                 $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
                 $propBlock->prefix = $name;
                 $foundSomething = true;
@@ -669,7 +669,7 @@ class Parser
         $this->seek($s);
 
         // closing a block
-        if ($this->literal('}')) {
+        if ($this->matchChar('}')) {
             $block = $this->popBlock();
 
             if (isset($block->type) && $block->type === Type::T_INCLUDE) {
@@ -686,8 +686,8 @@ class Parser
         }
 
         // extra stuff
-        if ($this->literal(';') ||
-            $this->literal('<!--')
+        if ($this->matchChar(';') ||
+            $this->literal('<!--', 4)
         ) {
             return true;
         }
@@ -763,11 +763,16 @@ class Parser
             $this->throwParseError('unexpected }');
         }
 
+        if ($block->type == Type::T_AT_ROOT) {
+            // keeps the parent in case of self selector &
+            $block->selfParent = $block->parent;
+        }
+
         $this->env = $block->parent;
         unset($block->parent);
 
         $comments = $block->comments;
-        if (count($comments)) {
+        if ($comments) {
             $this->env->comments = $comments;
             unset($block->comments);
         }
@@ -803,15 +808,9 @@ class Parser
      *
      * @return integer
      */
-    protected function seek($where = null)
+    protected function seek($where)
     {
-        if ($where === null) {
-            return $this->count;
-        }
-
         $this->count = $where;
-
-        return true;
     }
 
     /**
@@ -866,54 +865,84 @@ class Parser
      */
     protected function match($regex, &$out, $eatWhitespace = null)
     {
-        if (! isset($eatWhitespace)) {
-            $eatWhitespace = $this->eatWhiteDefault;
-        }
 
         $r = '/' . $regex . '/' . $this->patternModifiers;
 
-        if (preg_match($r, $this->buffer, $out, null, $this->count)) {
-            $this->count += strlen($out[0]);
+        if (! preg_match($r, $this->buffer, $out, null, $this->count)) {
+            return false;
+        }
 
-            if ($eatWhitespace) {
-                $this->whitespace();
-            }
+        $this->count += strlen($out[0]);
 
-            return true;
+        if (! isset($eatWhitespace)) {
+            $eatWhitespace = $this->eatWhiteDefault;
         }
 
-        return false;
+        if ($eatWhitespace) {
+            $this->whitespace();
+        }
+
+        return true;
     }
 
+
     /**
-     * Match literal string
+     * Match a single string
      *
-     * @param string  $what
+     * @param string  $char
      * @param boolean $eatWhitespace
      *
      * @return boolean
      */
-    protected function literal($what, $eatWhitespace = null)
+    protected function matchChar($char, $eatWhitespace = null)
     {
+
+        if (! isset($this->buffer[$this->count]) || $this->buffer[$this->count] !== $char) {
+            return false;
+        }
+
+        $this->count++;
+
         if (! isset($eatWhitespace)) {
             $eatWhitespace = $this->eatWhiteDefault;
         }
 
-        $len = strlen($what);
+        if ($eatWhitespace) {
+            $this->whitespace();
+        }
+        return true;
+    }
 
-        if (strcasecmp(substr($this->buffer, $this->count, $len), $what) === 0) {
-            $this->count += $len;
 
-            if ($eatWhitespace) {
-                $this->whitespace();
-            }
+    /**
+     * Match literal string
+     *
+     * @param string  $what
+     * @param integer $len
+     * @param boolean $eatWhitespace
+     *
+     * @return boolean
+     */
+    protected function literal($what, $len, $eatWhitespace = null)
+    {
 
-            return true;
+        if (strcasecmp(substr($this->buffer, $this->count, $len), $what) !== 0) {
+            return false;
         }
 
-        return false;
+        $this->count += $len;
+
+        if (! isset($eatWhitespace)) {
+            $eatWhitespace = $this->eatWhiteDefault;
+        }
+
+        if ($eatWhitespace) {
+            $this->whitespace();
+        }
+        return true;
     }
 
+
     /**
      * Match some whitespace
      *
@@ -969,7 +998,7 @@ class Parser
 
         $comments = $this->env->comments;
 
-        if (count($comments)) {
+        if ($comments) {
             $this->env->children = array_merge($this->env->children, $comments);
             $this->env->comments = [];
         }
@@ -1013,7 +1042,7 @@ class Parser
         $expressions = null;
         $parts = [];
 
-        if (($this->literal('only') && ($only = true) || $this->literal('not') && ($not = true) || true) &&
+        if (($this->literal('only', 4) && ($only = true) || $this->literal('not', 3) && ($not = true) || true) &&
             $this->mixedKeyword($mediaType)
         ) {
             $prop = [Type::T_MEDIA_TYPE];
@@ -1040,7 +1069,7 @@ class Parser
             $parts[] = $prop;
         }
 
-        if (empty($parts) || $this->literal('and')) {
+        if (empty($parts) || $this->literal('and', 3)) {
             $this->genericList($expressions, 'mediaExpression', 'and', false);
 
             if (is_array($expressions)) {
@@ -1062,13 +1091,13 @@ class Parser
      */
     protected function mediaExpression(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
         $value = null;
 
-        if ($this->literal('(') &&
+        if ($this->matchChar('(') &&
             $this->expression($feature) &&
-            ($this->literal(':') && $this->expression($value) || true) &&
-            $this->literal(')')
+            ($this->matchChar(':') && $this->expression($value) || true) &&
+            $this->matchChar(')')
         ) {
             $out = [Type::T_MEDIA_EXPRESSION, $feature];
 
@@ -1111,20 +1140,20 @@ class Parser
      */
     protected function argValue(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
         $keyword = null;
 
-        if (! $this->variable($keyword) || ! $this->literal(':')) {
+        if (! $this->variable($keyword) || ! $this->matchChar(':')) {
             $this->seek($s);
             $keyword = null;
         }
 
         if ($this->genericList($value, 'expression')) {
             $out = [$keyword, $value, false];
-            $s = $this->seek();
+            $s = $this->count;
 
-            if ($this->literal('...')) {
+            if ($this->literal('...', 3)) {
                 $out[2] = true;
             } else {
                 $this->seek($s);
@@ -1172,20 +1201,20 @@ class Parser
      */
     protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
     {
-        $s = $this->seek();
+        $s = $this->count;
         $items = [];
-
+        $value = null;
         while ($this->$parseItem($value)) {
             $items[] = $value;
 
             if ($delim) {
-                if (! $this->literal($delim)) {
+                if (! $this->literal($delim, strlen($delim))) {
                     break;
                 }
             }
         }
 
-        if (count($items) === 0) {
+        if (!$items) {
             $this->seek($s);
 
             return false;
@@ -1209,16 +1238,16 @@ class Parser
      */
     protected function expression(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
-        if ($this->literal('(')) {
-            if ($this->literal(')')) {
+        if ($this->matchChar('(')) {
+            if ($this->matchChar(')')) {
                 $out = [Type::T_LIST, '', []];
 
                 return true;
             }
 
-            if ($this->valueList($out) && $this->literal(')') && $out[0] === Type::T_LIST) {
+            if ($this->valueList($out) && $this->matchChar(')') && $out[0] === Type::T_LIST) {
                 return true;
             }
 
@@ -1252,7 +1281,7 @@ class Parser
     {
         $operators = static::$operatorPattern;
 
-        $ss = $this->seek();
+        $ss = $this->count;
         $whiteBefore = isset($this->buffer[$this->count - 1]) &&
             ctype_space($this->buffer[$this->count - 1]);
 
@@ -1281,7 +1310,7 @@ class Parser
             }
 
             $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
-            $ss = $this->seek();
+            $ss = $this->count;
             $whiteBefore = isset($this->buffer[$this->count - 1]) &&
                 ctype_space($this->buffer[$this->count - 1]);
         }
@@ -1300,14 +1329,20 @@ class Parser
      */
     protected function value(&$out)
     {
-        $s = $this->seek();
 
-        if ($this->literal('url(') && $this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m, false)) {
+        if (! isset($this->buffer[$this->count])) {
+            return false;
+        }
+
+        $s = $this->count;
+        $char = $this->buffer[$this->count];
+
+        if ($this->literal('url(', 4) && $this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m, false)) {
             $len = strspn($this->buffer, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=', $this->count);
 
             $this->count += $len;
 
-            if ($this->literal(')')) {
+            if ($this->matchChar(')')) {
                 $content = substr($this->buffer, $s, $this->count - $s);
                 $out = [Type::T_KEYWORD, $content];
 
@@ -1317,56 +1352,80 @@ class Parser
 
         $this->seek($s);
 
-        if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) {
-            $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
+        // not
+        if ($char === 'n' && $this->literal('not', 3, false)) {
+            if ($this->whitespace() && $this->value($inner)) {
+                $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
+                return true;
+            }
 
-            return true;
-        }
+            $this->seek($s);
 
-        $this->seek($s);
+            if ($this->parenValue($inner)) {
+                $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
+                return true;
+            }
 
-        if ($this->literal('not', false) && $this->parenValue($inner)) {
-            $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
+            $this->seek($s);
+        }
 
-            return true;
+        // addition
+        if ($char === '+') {
+            $this->count++;
+            if ($this->value($inner)) {
+                $out = [Type::T_UNARY, '+', $inner, $this->inParens];
+                return true;
+            }
+            $this->count--;
+            return false;
         }
 
-        $this->seek($s);
 
-        if ($this->literal('+') && $this->value($inner)) {
-            $out = [Type::T_UNARY, '+', $inner, $this->inParens];
+        // negation
+        if ($char === '-') {
+            $this->count++;
+            if ($this->variable($inner) || $this->unit($inner) || $this->parenValue($inner)) {
+                $out = [Type::T_UNARY, '-', $inner, $this->inParens];
+                return true;
+            }
+            $this->count--;
+        }
 
+        // paren
+        if ($char === '(' && $this->parenValue($out)) {
             return true;
         }
 
-        $this->seek($s);
+        if ($char === '#') {
+            if ($this->interpolation($out) || $this->color($out)) {
+                return true;
+            }
+        }
 
-        // negation
-        if ($this->literal('-', false) &&
-            ($this->variable($inner) ||
-            $this->unit($inner) ||
-            $this->parenValue($inner))
-        ) {
-            $out = [Type::T_UNARY, '-', $inner, $this->inParens];
+        if ($char === '$' && $this->variable($out)) {
+            return true;
+        }
 
+        if ($char === 'p' && $this->progid($out)) {
             return true;
         }
 
-        $this->seek($s);
+        if (($char === '"' || $char === "'") && $this->string($out)) {
+            return true;
+        }
 
-        if ($this->parenValue($out) ||
-            $this->interpolation($out) ||
-            $this->variable($out) ||
-            $this->color($out) ||
-            $this->unit($out) ||
-            $this->string($out) ||
-            $this->func($out) ||
-            $this->progid($out)
-        ) {
+
+        if ($this->unit($out)) {
             return true;
         }
 
-        if ($this->keyword($keyword)) {
+        if ($this->keyword($keyword, false)) {
+            if ($this->func($keyword, $out)) {
+                return true;
+            }
+
+            $this->whitespace();
+
             if ($keyword === 'null') {
                 $out = [Type::T_NULL];
             } else {
@@ -1388,12 +1447,12 @@ class Parser
      */
     protected function parenValue(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
         $inParens = $this->inParens;
 
-        if ($this->literal('(')) {
-            if ($this->literal(')')) {
+        if ($this->matchChar('(')) {
+            if ($this->matchChar(')')) {
                 $out = [Type::T_LIST, '', []];
 
                 return true;
@@ -1401,7 +1460,7 @@ class Parser
 
             $this->inParens = true;
 
-            if ($this->expression($exp) && $this->literal(')')) {
+            if ($this->expression($exp) && $this->matchChar(')')) {
                 $out = $exp;
                 $this->inParens = $inParens;
 
@@ -1424,15 +1483,15 @@ class Parser
      */
     protected function progid(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
-        if ($this->literal('progid:', false) &&
+        if ($this->literal('progid:', 7, false) &&
             $this->openString('(', $fn) &&
-            $this->literal('(')
+            $this->matchChar('(')
         ) {
             $this->openString(')', $args, '(');
 
-            if ($this->literal(')')) {
+            if ($this->matchChar(')')) {
                 $out = [Type::T_STRING, '', [
                     'progid:', $fn, '(', $args, ')'
                 ]];
@@ -1449,17 +1508,16 @@ class Parser
     /**
      * Parse function call
      *
+     * @param string $name
      * @param array $out
      *
      * @return boolean
      */
-    protected function func(&$func)
+    protected function func($name, &$func)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
-        if ($this->keyword($name, false) &&
-            $this->literal('(')
-        ) {
+        if ($this->matchChar('(')) {
             if ($name === 'alpha' && $this->argumentList($args)) {
                 $func = [Type::T_FUNCTION, $name, [Type::T_STRING, '', $args]];
 
@@ -1467,9 +1525,9 @@ class Parser
             }
 
             if ($name !== 'expression' && ! preg_match('/^(-[a-z]+-)?calc$/', $name)) {
-                $ss = $this->seek();
+                $ss = $this->count;
 
-                if ($this->argValues($args) && $this->literal(')')) {
+                if ($this->argValues($args) && $this->matchChar(')')) {
                     $func = [Type::T_FUNCTION_CALL, $name, $args];
 
                     return true;
@@ -1479,7 +1537,7 @@ class Parser
             }
 
             if (($this->openString(')', $str, '(') || true) &&
-                $this->literal(')')
+                $this->matchChar(')')
             ) {
                 $args = [];
 
@@ -1507,13 +1565,13 @@ class Parser
      */
     protected function argumentList(&$out)
     {
-        $s = $this->seek();
-        $this->literal('(');
+        $s = $this->count;
+        $this->matchChar('(');
 
         $args = [];
 
         while ($this->keyword($var)) {
-            if ($this->literal('=') && $this->expression($exp)) {
+            if ($this->matchChar('=') && $this->expression($exp)) {
                 $args[] = [Type::T_STRING, '', [$var . '=']];
                 $arg = $exp;
             } else {
@@ -1522,14 +1580,14 @@ class Parser
 
             $args[] = $arg;
 
-            if (! $this->literal(',')) {
+            if (! $this->matchChar(',')) {
                 break;
             }
 
             $args[] = [Type::T_STRING, '', [', ']];
         }
 
-        if (! $this->literal(')') || ! count($args)) {
+        if (! $this->matchChar(')') || !$args) {
             $this->seek($s);
 
             return false;
@@ -1549,28 +1607,28 @@ class Parser
      */
     protected function argumentDef(&$out)
     {
-        $s = $this->seek();
-        $this->literal('(');
+        $s = $this->count;
+        $this->matchChar('(');
 
         $args = [];
 
         while ($this->variable($var)) {
             $arg = [$var[1], null, false];
 
-            $ss = $this->seek();
+            $ss = $this->count;
 
-            if ($this->literal(':') && $this->genericList($defaultVal, 'expression')) {
+            if ($this->matchChar(':') && $this->genericList($defaultVal, 'expression')) {
                 $arg[1] = $defaultVal;
             } else {
                 $this->seek($ss);
             }
 
-            $ss = $this->seek();
+            $ss = $this->count;
 
-            if ($this->literal('...')) {
-                $sss = $this->seek();
+            if ($this->literal('...', 3)) {
+                $sss = $this->count;
 
-                if (! $this->literal(')')) {
+                if (! $this->matchChar(')')) {
                     $this->throwParseError('... has to be after the final argument');
                 }
 
@@ -1582,12 +1640,12 @@ class Parser
 
             $args[] = $arg;
 
-            if (! $this->literal(',')) {
+            if (! $this->matchChar(',')) {
                 break;
             }
         }
 
-        if (! $this->literal(')')) {
+        if (! $this->matchChar(')')) {
             $this->seek($s);
 
             return false;
@@ -1607,27 +1665,27 @@ class Parser
      */
     protected function map(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
-        if (! $this->literal('(')) {
+        if (! $this->matchChar('(')) {
             return false;
         }
 
         $keys = [];
         $values = [];
 
-        while ($this->genericList($key, 'expression') && $this->literal(':') &&
+        while ($this->genericList($key, 'expression') && $this->matchChar(':') &&
             $this->genericList($value, 'expression')
         ) {
             $keys[] = $key;
             $values[] = $value;
 
-            if (! $this->literal(',')) {
+            if (! $this->matchChar(',')) {
                 break;
             }
         }
 
-        if (! count($keys) || ! $this->literal(')')) {
+        if (!$keys || ! $this->matchChar(')')) {
             $this->seek($s);
 
             return false;
@@ -1648,22 +1706,46 @@ class Parser
     protected function color(&$out)
     {
         $color = [Type::T_COLOR];
+        $s     = $this->count;
+
+        if ($this->match('(#([0-9a-f]+))', $m)) {
+            $nofValues = strlen($m[2]);
+            $hasAlpha  = $nofValues === 4 || $nofValues === 8;
+            $channels  = $hasAlpha ? [4, 3, 2, 1] : [3, 2, 1];
+
+            switch ($nofValues) {
+                case 3:
+                case 4:
+                    $num = hexdec($m[2]);
+
+                    foreach ($channels as $i) {
+                        $t = $num & 0xf;
+                        $color[$i] = $t << 4 | $t;
+                        $num >>= 4;
+                    }
+                    break;
 
-        if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
-            if (isset($m[3])) {
-                $num = hexdec($m[3]);
+                case 6:
+                case 8:
+                    $num = hexdec($m[2]);
 
-                foreach ([3, 2, 1] as $i) {
-                    $t = $num & 0xf;
-                    $color[$i] = $t << 4 | $t;
-                    $num >>= 4;
-                }
-            } else {
-                $num = hexdec($m[2]);
+                    foreach ($channels as $i) {
+                        $color[$i] = $num & 0xff;
+                        $num >>= 8;
+                    }
+                    break;
+
+                default:
+                    $this->seek($s);
+
+                    return false;
+            }
 
-                foreach ([3, 2, 1] as $i) {
-                    $color[$i] = $num & 0xff;
-                    $num >>= 8;
+            if ($hasAlpha) {
+                if ($color[4] === 255) {
+                    $color[4] = 1; // fully opaque
+                } else {
+                    $color[4] = round($color[4] / 255, 3);
                 }
             }
 
@@ -1702,11 +1784,11 @@ class Parser
      */
     protected function string(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
-        if ($this->literal('"', false)) {
+        if ($this->matchChar('"', false)) {
             $delim = '"';
-        } elseif ($this->literal("'", false)) {
+        } elseif ($this->matchChar("'", false)) {
             $delim = "'";
         } else {
             return false;
@@ -1733,10 +1815,12 @@ class Parser
                     $content[] = '#{'; // ignore it
                 }
             } elseif ($m[2] === '\\') {
-                if ($this->literal('"', false)) {
+                if ($this->matchChar('"', false)) {
                     $content[] = $m[2] . '"';
-                } elseif ($this->literal("'", false)) {
+                } elseif ($this->matchChar("'", false)) {
                     $content[] = $m[2] . "'";
+                } elseif ($this->literal("\\", 1, false)) {
+                    $content[] = $m[2] . "\\";
                 } else {
                     $content[] = $m[2];
                 }
@@ -1748,12 +1832,14 @@ class Parser
 
         $this->eatWhiteDefault = $oldWhite;
 
-        if ($this->literal($delim)) {
+        if ($this->literal($delim, strlen($delim))) {
             if ($hasInterpolation) {
                 $delim = '"';
 
                 foreach ($content as &$string) {
-                    if ($string === "\\'") {
+                    if ($string === "\\\\") {
+                        $string = "\\";
+                    } elseif ($string === "\\'") {
                         $string = "'";
                     } elseif ($string === '\\"') {
                         $string = '"';
@@ -1801,7 +1887,7 @@ class Parser
 
         $this->eatWhiteDefault = $oldWhite;
 
-        if (count($parts) === 0) {
+        if (!$parts) {
             return false;
         }
 
@@ -1867,7 +1953,7 @@ class Parser
 
         $this->eatWhiteDefault = $oldWhite;
 
-        if (count($content) === 0) {
+        if (!$content) {
             return false;
         }
 
@@ -1894,9 +1980,9 @@ class Parser
         $oldWhite = $this->eatWhiteDefault;
         $this->eatWhiteDefault = true;
 
-        $s = $this->seek();
+        $s = $this->count;
 
-        if ($this->literal('#{') && $this->valueList($value) && $this->literal('}', false)) {
+        if ($this->literal('#{', 2) && $this->valueList($value) && $this->matchChar('}', false)) {
             if ($lookWhite) {
                 $left = preg_match('/\s/', $this->buffer[$s - 1]) ? ' ' : '';
                 $right = preg_match('/\s/', $this->buffer[$this->count]) ? ' ': '';
@@ -1914,6 +2000,20 @@ class Parser
             return true;
         }
 
+        $this->seek($s);
+
+        if ($this->literal('#{', 2) && $this->selectorSingle($sel) && $this->matchChar('}', false)) {
+            $out = $sel[0];
+
+            $this->eatWhiteDefault = $oldWhite;
+
+            if ($this->eatWhiteDefault) {
+                $this->whitespace();
+            }
+
+            return true;
+        }
+
         $this->seek($s);
         $this->eatWhiteDefault = $oldWhite;
 
@@ -1945,7 +2045,7 @@ class Parser
                 continue;
             }
 
-            if (count($parts) === 0 && $this->match('[:.#]', $m, false)) {
+            if (!$parts && $this->match('[:.#]', $m, false)) {
                 // css hacks
                 $parts[] = $m[0];
                 continue;
@@ -1956,7 +2056,7 @@ class Parser
 
         $this->eatWhiteDefault = $oldWhite;
 
-        if (count($parts) === 0) {
+        if (!$parts) {
             return false;
         }
 
@@ -1990,22 +2090,22 @@ class Parser
      */
     protected function selectors(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
         $selectors = [];
 
         while ($this->selector($sel)) {
             $selectors[] = $sel;
 
-            if (! $this->literal(',')) {
+            if (! $this->matchChar(',')) {
                 break;
             }
 
-            while ($this->literal(',')) {
+            while ($this->matchChar(',')) {
                 ; // ignore extra
             }
         }
 
-        if (count($selectors) === 0) {
+        if (!$selectors) {
             $this->seek($s);
 
             return false;
@@ -2047,7 +2147,7 @@ class Parser
             break;
         }
 
-        if (count($selector) === 0) {
+        if (!$selector) {
             return false;
         }
 
@@ -2073,101 +2173,115 @@ class Parser
 
         $parts = [];
 
-        if ($this->literal('*', false)) {
+        if ($this->matchChar('*', false)) {
             $parts[] = '*';
         }
 
         for (;;) {
-            // see if we can stop early
-            if ($this->match('\s*[{,]', $m)) {
-                $this->count--;
+            if (! isset($this->buffer[$this->count])) {
                 break;
             }
 
-            $s = $this->seek();
+            $s = $this->count;
+            $char = $this->buffer[$this->count];
 
-            // self
-            if ($this->literal('&', false)) {
-                $parts[] = Compiler::$selfSelector;
-                continue;
+            // see if we can stop early
+            if ($char === '{' || $char === ',' || $char === ';' || $char === '}' || $char === '@') {
+                break;
             }
 
-            if ($this->literal('.', false)) {
-                $parts[] = '.';
-                continue;
-            }
 
-            if ($this->literal('|', false)) {
-                $parts[] = '|';
-                continue;
+            //self
+            switch ($char) {
+                case '&':
+                    $parts[] = Compiler::$selfSelector;
+                    $this->count++;
+                    continue 2;
+                case '.':
+                    $parts[] = '.';
+                    $this->count++;
+                    continue 2;
+                case '|':
+                    $parts[] = '|';
+                    $this->count++;
+                    continue 2;
             }
 
-            if ($this->match('\\\\\S', $m)) {
-                $parts[] = $m[0];
-                continue;
-            }
 
-            // for keyframes
-            if ($this->unit($unit)) {
-                $parts[] = $unit;
+            if ($char === '\\' && $this->match('\\\\\S', $m)) {
+                $parts[] = $m[0];
                 continue;
             }
 
-            if ($this->keyword($name)) {
-                $parts[] = $name;
-                continue;
-            }
 
-            if ($this->interpolation($inter)) {
-                $parts[] = $inter;
-                continue;
+            if ($char === '%') {
+                $this->count++;
+                if ($this->placeholder($placeholder)) {
+                    $parts[] = '%';
+                    $parts[] = $placeholder;
+                    continue;
+                }
+                break;
             }
 
-            if ($this->literal('%', false) && $this->placeholder($placeholder)) {
-                $parts[] = '%';
-                $parts[] = $placeholder;
-                continue;
-            }
+            if ($char === '#') {
+                if ($this->interpolation($inter)) {
+                    $parts[] = $inter;
+                    continue;
+                }
 
-            if ($this->literal('#', false)) {
                 $parts[] = '#';
+                $this->count++;
                 continue;
             }
 
-            // a pseudo selector
-            if ($this->match('::?', $m) && $this->mixedKeyword($nameParts)) {
-                $parts[] = $m[0];
 
-                foreach ($nameParts as $sub) {
-                    $parts[] = $sub;
+            // a pseudo selector
+            if ($char === ':') {
+                if ($this->buffer[$this->count + 1] === ':') {
+                    $this->count += 2;
+                    $part = '::';
+                } else {
+                    $this->count++;
+                    $part = ':';
                 }
+                if ($this->mixedKeyword($nameParts)) {
+                    $parts[] = $part;
+
+                    foreach ($nameParts as $sub) {
+                        $parts[] = $sub;
+                    }
+
+                    $ss = $this->count;
 
-                $ss = $this->seek();
+                    if ($this->matchChar('(') &&
+                      ($this->openString(')', $str, '(') || true) &&
+                      $this->matchChar(')')
+                    ) {
+                        $parts[] = '(';
 
-                if ($this->literal('(') &&
-                    ($this->openString(')', $str, '(') || true) &&
-                    $this->literal(')')
-                ) {
-                    $parts[] = '(';
+                        if (! empty($str)) {
+                            $parts[] = $str;
+                        }
 
-                    if (! empty($str)) {
-                        $parts[] = $str;
+                        $parts[] = ')';
+                    } else {
+                        $this->seek($ss);
                     }
 
-                    $parts[] = ')';
-                } else {
-                    $this->seek($ss);
+                    continue;
                 }
-
-                continue;
             }
 
+
             $this->seek($s);
 
+
             // attribute selector
-            if ($this->literal('[') &&
-               ($this->openString(']', $str, '[') || true) &&
-               $this->literal(']')
+            if ($char === '[' &&
+              $this->matchChar('[') &&
+              ($this->openString(']', $str, '[') || true) &&
+              $this->matchChar(']')
             ) {
                 $parts[] = '[';
 
@@ -2182,12 +2296,27 @@ class Parser
 
             $this->seek($s);
 
+
+            // for keyframes
+            if ($this->unit($unit)) {
+                $parts[] = $unit;
+                continue;
+            }
+
+            if ($this->keyword($name)) {
+                $parts[] = $name;
+                continue;
+            }
+
+
+
+
             break;
         }
 
         $this->eatWhiteDefault = $oldWhite;
 
-        if (count($parts) === 0) {
+        if (!$parts) {
             return false;
         }
 
@@ -2205,9 +2334,9 @@ class Parser
      */
     protected function variable(&$out)
     {
-        $s = $this->seek();
+        $s = $this->count;
 
-        if ($this->literal('$', false) && $this->keyword($name)) {
+        if ($this->matchChar('$', false) && $this->keyword($name)) {
             $out = [Type::T_VARIABLE, $name];
 
             return true;
@@ -2291,7 +2420,7 @@ class Parser
      */
     protected function end()
     {
-        if ($this->literal(';')) {
+        if ($this->matchChar(';')) {
             return true;
         }
 
diff --git a/wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/Base64.php b/wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/Base64.php
new file mode 100644 (file)
index 0000000..54015bb
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+/**
+ * SCSSPHP
+ *
+ * @copyright 2012-2015 Leaf Corcoran
+ *
+ * @license http://opensource.org/licenses/MIT MIT
+ *
+ * @link http://leafo.github.io/scssphp
+ */
+
+namespace Leafo\ScssPhp\SourceMap;
+
+/**
+ * Base 64 Encode/Decode
+ *
+ * @author Anthon Pang <anthon.pang@gmail.com>
+ */
+class Base64
+{
+    /**
+     * @var array
+     */
+    private static $encodingMap = [
+        0 => 'A',
+        1 => 'B',
+        2 => 'C',
+        3 => 'D',
+        4 => 'E',
+        5 => 'F',
+        6 => 'G',
+        7 => 'H',
+        8 => 'I',
+        9 => 'J',
+        10 => 'K',
+        11 => 'L',
+        12 => 'M',
+        13 => 'N',
+        14 => 'O',
+        15 => 'P',
+        16 => 'Q',
+        17 => 'R',
+        18 => 'S',
+        19 => 'T',
+        20 => 'U',
+        21 => 'V',
+        22 => 'W',
+        23 => 'X',
+        24 => 'Y',
+        25 => 'Z',
+        26 => 'a',
+        27 => 'b',
+        28 => 'c',
+        29 => 'd',
+        30 => 'e',
+        31 => 'f',
+        32 => 'g',
+        33 => 'h',
+        34 => 'i',
+        35 => 'j',
+        36 => 'k',
+        37 => 'l',
+        38 => 'm',
+        39 => 'n',
+        40 => 'o',
+        41 => 'p',
+        42 => 'q',
+        43 => 'r',
+        44 => 's',
+        45 => 't',
+        46 => 'u',
+        47 => 'v',
+        48 => 'w',
+        49 => 'x',
+        50 => 'y',
+        51 => 'z',
+        52 => '0',
+        53 => '1',
+        54 => '2',
+        55 => '3',
+        56 => '4',
+        57 => '5',
+        58 => '6',
+        59 => '7',
+        60 => '8',
+        61 => '9',
+        62 => '+',
+        63 => '/',
+    ];
+
+    /**
+     * @var array
+     */
+    private static $decodingMap = [
+        'A' => 0,
+        'B' => 1,
+        'C' => 2,
+        'D' => 3,
+        'E' => 4,
+        'F' => 5,
+        'G' => 6,
+        'H' => 7,
+        'I' => 8,
+        'J' => 9,
+        'K' => 10,
+        'L' => 11,
+        'M' => 12,
+        'N' => 13,
+        'O' => 14,
+        'P' => 15,
+        'Q' => 16,
+        'R' => 17,
+        'S' => 18,
+        'T' => 19,
+        'U' => 20,
+        'V' => 21,
+        'W' => 22,
+        'X' => 23,
+        'Y' => 24,
+        'Z' => 25,
+        'a' => 26,
+        'b' => 27,
+        'c' => 28,
+        'd' => 29,
+        'e' => 30,
+        'f' => 31,
+        'g' => 32,
+        'h' => 33,
+        'i' => 34,
+        'j' => 35,
+        'k' => 36,
+        'l' => 37,
+        'm' => 38,
+        'n' => 39,
+        'o' => 40,
+        'p' => 41,
+        'q' => 42,
+        'r' => 43,
+        's' => 44,
+        't' => 45,
+        'u' => 46,
+        'v' => 47,
+        'w' => 48,
+        'x' => 49,
+        'y' => 50,
+        'z' => 51,
+        0 => 52,
+        1 => 53,
+        2 => 54,
+        3 => 55,
+        4 => 56,
+        5 => 57,
+        6 => 58,
+        7 => 59,
+        8 => 60,
+        9 => 61,
+        '+' => 62,
+        '/' => 63,
+    ];
+
+    /**
+     * Convert to base64
+     *
+     * @param integer $value
+     *
+     * @return string
+     */
+    public static function encode($value)
+    {
+        return self::$encodingMap[$value];
+    }
+
+    /**
+     * Convert from base64
+     *
+     * @param string $value
+     *
+     * @return integer
+     */
+    public static function decode($value)
+    {
+        return self::$decodingMap[$value];
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/Base64VLQ.php b/wcfsetup/install/files/lib/system/api/leafo/scssphp/src/SourceMap/Base64VLQ.php
new file mode 100644 (file)
index 0000000..6de3b5e
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/**
+ * SCSSPHP
+ *
+ * @copyright 2012-2015 Leaf Corcoran
+ *
+ * @license http://opensource.org/licenses/MIT MIT
+ *
+ * @link http://leafo.github.io/scssphp
+ */
+
+namespace Leafo\ScssPhp\SourceMap;
+
+use Leafo\ScssPhp\SourceMap\Base64;
+
+/**
+ * Base 64 VLQ
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://github.com/google/closure-compiler/blob/master/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author John Lenz <johnlenz@google.com>
+ * @author Anthon Pang <anthon.pang@gmail.com>
+ */
+class Base64VLQ
+{
+    // A Base64 VLQ digit can represent 5 bits, so it is base-32.
+    const VLQ_BASE_SHIFT = 5;
+
+    // A mask of bits for a VLQ digit (11111), 31 decimal.
+    const VLQ_BASE_MASK = 31;
+
+    // The continuation bit is the 6th bit.
+    const VLQ_CONTINUATION_BIT = 32;
+
+    /**
+     * Returns the VLQ encoded value.
+     *
+     * @param integer $value
+     *
+     * @return string
+     */
+    public static function encode($value)
+    {
+        $encoded = '';
+        $vlq = self::toVLQSigned($value);
+
+        do {
+            $digit = $vlq & self::VLQ_BASE_MASK;
+            $vlq >>= self::VLQ_BASE_SHIFT;
+
+            if ($vlq > 0) {
+                $digit |= self::VLQ_CONTINUATION_BIT;
+            }
+
+            $encoded .= Base64::encode($digit);
+        } while ($vlq > 0);
+
+        return $encoded;
+    }
+
+    /**
+     * Decodes VLQValue.
+     *
+     * @param string $str
+     * @param integer $index
+     *
+     * @return integer
+     */
+    public static function decode($str, &$index)
+    {
+        $result = 0;
+        $shift = 0;
+
+        do {
+            $c = $str[$index++];
+            $digit = Base64::decode($c);
+            $continuation = ($digit & self::VLQ_CONTINUATION_BIT) != 0;
+            $digit &= self::VLQ_BASE_MASK;
+            $result = $result + ($digit << $shift);
+            $shift = $shift + self::VLQ_BASE_SHIFT;
+        } while ($continuation);
+
+        return self::fromVLQSigned($result);
+    }
+
+    /**
+     * Converts from a two-complement value to a value where the sign bit is
+     * is placed in the least significant bit.  For example, as decimals:
+     *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+     *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+     *
+     * @param integer $value
+     *
+     * @return integer
+     */
+    private static function toVLQSigned($value)
+    {
+        if ($value < 0) {
+            return ((-$value) << 1) + 1;
+        }
+
+        return ($value << 1) + 0;
+    }
+
+    /**
+     * Converts to a two-complement value from a value where the sign bit is
+     * is placed in the least significant bit.  For example, as decimals:
+     *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+     *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+     *
+     * @param integer $value
+     *
+     * @return integer
+     */
+    private static function fromVLQSigned($value)
+    {
+        $negate = ($value & 1) === 1;
+        $value = $value >> 1;
+
+        return $negate ? -$value : $value;
+    }
+}
index 1189ce0e5a53001ddc7174f4b2989ef8371c9366..caf56c7716e85fe3a2f0dceb81d8643778b34525 100644 (file)
@@ -47,7 +47,7 @@ class Base64VLQEncoder
      *
      * @var array
      */
-    private $charToIntMap = array(
+    private $charToIntMap = [
         'A' => 0,  'B' => 1,  'C' => 2,  'D' => 3,  'E' => 4,  'F' => 5,  'G' => 6,  'H' => 7,
         'I' => 8,  'J' => 9,  'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15,
         'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23,
@@ -56,14 +56,14 @@ class Base64VLQEncoder
         'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47,
         'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51,   0 => 52,   1 => 53,   2 => 54,   3 => 55,
           4 => 56,   5 => 57,   6 => 58,   7 => 59,   8 => 60,   9 => 61, '+' => 62, '/' => 63,
-    );
+    ];
 
     /**
      * Integer to char map
      *
      * @var array
      */
-    private $intToCharMap = array(
+    private $intToCharMap = [
          0 => 'A',  1 => 'B',  2 => 'C',  3 => 'D',  4 => 'E',  5 => 'F',  6 => 'G',  7 => 'H',
          8 => 'I',  9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P',
         16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X',
@@ -72,7 +72,7 @@ class Base64VLQEncoder
         40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v',
         48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
         56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/',
-    );
+    ];
 
     /**
      * Constructor
index 70b47cdc7a49225c7620bcf1c2e26e9723537c7b..3c636a88728adab0d783e81aeee0bacca9fb3b8b 100644 (file)
@@ -33,7 +33,7 @@ class SourceMapGenerator
      *
      * @var array
      */
-    protected $defaultOptions = array(
+    protected $defaultOptions = [
         // an optional source root, useful for relocating source files
         // on a server or removing repeated values in the 'sources' entry.
         // This value is prepended to the individual entries in the 'source' field.
@@ -56,12 +56,12 @@ class SourceMapGenerator
 
         // base path for filename normalization
         'sourceMapBasepath' => ''
-    );
+    ];
 
     /**
      * The base64 VLQ encoder
      *
-     * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder
+     * @var \Leafo\ScssPhp\SourceMap\Base64VLQ
      */
     protected $encoder;
 
@@ -70,22 +70,22 @@ class SourceMapGenerator
      *
      * @var array
      */
-    protected $mappings = array();
+    protected $mappings = [];
 
     /**
      * Array of contents map
      *
      * @var array
      */
-    protected $contentsMap = array();
+    protected $contentsMap = [];
 
     /**
      * File to content map
      *
      * @var array
      */
-    protected $sources = array();
-    protected $source_keys = array();
+    protected $sources = [];
+    protected $source_keys = [];
 
     /**
      * @var array
@@ -95,7 +95,7 @@ class SourceMapGenerator
     public function __construct(array $options = [])
     {
         $this->options = array_merge($this->defaultOptions, $options);
-        $this->encoder = new Base64VLQEncoder();
+        $this->encoder = new Base64VLQ();
     }
 
     /**
@@ -109,13 +109,13 @@ class SourceMapGenerator
      */
     public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile)
     {
-        $this->mappings[] = array(
+        $this->mappings[] = [
             'generated_line' => $generatedLine,
             'generated_column' => $generatedColumn,
             'original_line' => $originalLine,
             'original_column' => $originalColumn,
             'source_file' => $sourceFile
-        );
+        ];
 
         $this->sources[$sourceFile] = $sourceFile;
     }
@@ -156,7 +156,7 @@ class SourceMapGenerator
      */
     public function generateJson()
     {
-        $sourceMap = array();
+        $sourceMap = [];
         $mappings  = $this->generateMappings();
 
         // File version (always the first entry in the object) and must be a positive integer.
@@ -178,14 +178,14 @@ class SourceMapGenerator
         }
 
         // A list of original sources used by the 'mappings' entry.
-        $sourceMap['sources'] = array();
+        $sourceMap['sources'] = [];
 
         foreach ($this->sources as $source_uri => $source_filename) {
             $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
         }
 
         // A list of symbol names used by the 'mappings' entry.
-        $sourceMap['names'] = array();
+        $sourceMap['names'] = [];
 
         // A string with the encoded mapping data.
         $sourceMap['mappings'] = $mappings;
@@ -202,7 +202,7 @@ class SourceMapGenerator
             unset($sourceMap['sourceRoot']);
         }
 
-        return json_encode($sourceMap);
+        return json_encode($sourceMap, JSON_UNESCAPED_SLASHES);
     }
 
     /**
@@ -216,7 +216,7 @@ class SourceMapGenerator
             return null;
         }
 
-        $content = array();
+        $content = [];
 
         foreach ($this->sources as $sourceFile) {
             $content[] = file_get_contents($sourceFile);
@@ -239,7 +239,7 @@ class SourceMapGenerator
         $this->source_keys = array_flip(array_keys($this->sources));
 
         // group mappings by generated line number.
-        $groupedMap = $groupedMapEncoded = array();
+        $groupedMap = $groupedMapEncoded = [];
 
         foreach ($this->mappings as $m) {
             $groupedMap[$m['generated_line']][] = $m;
@@ -253,7 +253,7 @@ class SourceMapGenerator
                 $groupedMapEncoded[] = ';';
             }
 
-            $lineMapEncoded = array();
+            $lineMapEncoded = [];
             $lastGeneratedColumn = 0;
 
             foreach ($line_map as $m) {
@@ -303,7 +303,7 @@ class SourceMapGenerator
         $basePath = $this->options['sourceMapBasepath'];
 
         // "Trim" the 'sourceMapBasepath' from the output filename.
-        if (strpos($filename, $basePath) === 0) {
+        if (strlen($basePath) && strpos($filename, $basePath) === 0) {
             $filename = substr($filename, strlen($basePath));
         }
 
index 7526e020566b6af35be663ca8e65e7789a15f5a3..a1e50658876bb32a2f9a84f57c33f40282a6dfe4 100644 (file)
@@ -63,7 +63,7 @@ class Util
      */
     public static function encodeURIComponent($string)
     {
-        $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
+        $revert = ['%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'];
 
         return strtr(rawurlencode($string), $revert);
     }
index c8a56a5b487ed86034367d1ddb373920a4591f71..ec0fee6f410272f5babbbe4f2b916960109c3d05 100644 (file)
@@ -18,5 +18,5 @@ namespace Leafo\ScssPhp;
  */
 class Version
 {
-    const VERSION = 'v0.7.6';
+    const VERSION = 'v0.7.8';
 }
index cb3158a5536dc73b7652526db831c47bfdde945d..1200c979ea6acd0dfd0e72dac5a12d3f35224ed8 100644 (file)
@@ -25,7 +25,6 @@ class SyntaxErrorException extends ParseException
 {
     /**
      * @param string $expectedValue
-     * @param Token  $foundToken
      *
      * @return self
      */
index 1caccb6bfeefb62ad7fc11f86dae845e89e36aa2..c09d92477e2a0dfb8233e133a1821d43494c3920 100644 (file)
@@ -30,11 +30,10 @@ class AttributeNode extends AbstractNode
     private $value;
 
     /**
-     * @param NodeInterface $selector
-     * @param string        $namespace
-     * @param string        $attribute
-     * @param string        $operator
-     * @param string        $value
+     * @param string $namespace
+     * @param string $attribute
+     * @param string $operator
+     * @param string $value
      */
     public function __construct(NodeInterface $selector, $namespace, $attribute, $operator, $value)
     {
index 69462e8e7109112d70d706bcc384bd4a5a54a7c6..866607cba61dbaa94c2d606750b5856a1fca6686 100644 (file)
@@ -27,8 +27,7 @@ class ClassNode extends AbstractNode
     private $name;
 
     /**
-     * @param NodeInterface $selector
-     * @param string        $name
+     * @param string $name
      */
     public function __construct(NodeInterface $selector, $name)
     {
index 2aa583aaf69283e7384992e6c205140ba8b991f9..27b0224857e31f5f69c39cec0887fc861cfe4c34 100644 (file)
@@ -28,9 +28,7 @@ class CombinedSelectorNode extends AbstractNode
     private $subSelector;
 
     /**
-     * @param NodeInterface $selector
-     * @param string        $combinator
-     * @param NodeInterface $subSelector
+     * @param string $combinator
      */
     public function __construct(NodeInterface $selector, $combinator, NodeInterface $subSelector)
     {
index 677affaa98d7c3f371442c5944756a52ee4a65e0..b6f95e975d776e09864facb6b58f0b48c48e41f1 100644 (file)
@@ -30,9 +30,8 @@ class FunctionNode extends AbstractNode
     private $arguments;
 
     /**
-     * @param NodeInterface $selector
-     * @param string        $name
-     * @param Token[]       $arguments
+     * @param string  $name
+     * @param Token[] $arguments
      */
     public function __construct(NodeInterface $selector, $name, array $arguments = [])
     {
index ebf9a9872a7d1b92b0c1325689821790860b5ca6..3ea89dc18389698152583bc1465de8285237fca8 100644 (file)
@@ -27,8 +27,7 @@ class HashNode extends AbstractNode
     private $id;
 
     /**
-     * @param NodeInterface $selector
-     * @param string        $id
+     * @param string $id
      */
     public function __construct(NodeInterface $selector, $id)
     {
index 3842c695e852bb81f48b5e628fe6a4a2721aad2c..c7d0b9fb3c17c0b4a82d655a87f36cc451cbdae0 100644 (file)
@@ -27,8 +27,7 @@ class PseudoNode extends AbstractNode
     private $identifier;
 
     /**
-     * @param NodeInterface $selector
-     * @param string        $identifier
+     * @param string $identifier
      */
     public function __construct(NodeInterface $selector, $identifier)
     {
index 057107f6f5b5704a9f0197bc54ae762b315f517a..2379babffd841221f73b1e849f9cf22ce606f199 100644 (file)
@@ -27,8 +27,7 @@ class SelectorNode extends AbstractNode
     private $pseudoElement;
 
     /**
-     * @param NodeInterface $tree
-     * @param string|null   $pseudoElement
+     * @param string|null $pseudoElement
      */
     public function __construct(NodeInterface $tree, $pseudoElement = null)
     {
index f07985ac52aba1365f87eadcff0f57117af321ee..7b131efdbaf25794e09f1b7cb775626d17abc2e1 100644 (file)
@@ -158,8 +158,7 @@ class Parser implements ParserInterface
     /**
      * Parses next simple node (hash, class, pseudo, negation).
      *
-     * @param TokenStream $stream
-     * @param bool        $insideNegation
+     * @param bool $insideNegation
      *
      * @return array
      *
index 843e330b523e084d6a93bfc675e82066627ffb42..001bfe1a7662c09f5fb3db79da74c6d41360aad1 100644 (file)
@@ -155,7 +155,7 @@ class TokenStream
         }
 
         if ($next->isDelimiter(['*'])) {
-            return;
+            return null;
         }
 
         throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next);
index 55ea42149329e518ab0137a928491a3765310cb2..ce322e96fdf92049ec41beb63a763cc400199b5f 100644 (file)
@@ -73,6 +73,8 @@ class TokenizerEscaping
             if (0x10000 > $c) {
                 return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
             }
+
+            return '';
         }, $value);
     }
 }
index cd0fd785e53d844652f7596342d85c6e39f23b81..82e527c62e78bbefbac7578ae38af2b85dcb5eaf 100644 (file)
@@ -35,12 +35,10 @@ class CssSelectorConverterTest extends TestCase
         $this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1'));
     }
 
-    /**
-     * @expectedException \Symfony\Component\CssSelector\Exception\ParseException
-     * @expectedExceptionMessage Expected identifier, but <eof at 3> found.
-     */
     public function testParseExceptions()
     {
+        $this->expectException('Symfony\Component\CssSelector\Exception\ParseException');
+        $this->expectExceptionMessage('Expected identifier, but <eof at 3> found.');
         $converter = new CssSelectorConverter();
         $converter->toXPath('h1:');
     }
index de3509348c05158b3945fd87e8d106006c0d7d96..f63a15e0ab728a9d469aec5dfa57e6d32766297f 100644 (file)
@@ -89,7 +89,7 @@ class ParserTest extends TestCase
 
         /** @var FunctionNode $function */
         $function = $selectors[0]->getTree();
-        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
+        $this->expectException('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
         Parser::parseSeries($function->getArguments());
     }
 
index 44c751ac865d2f98384759635436c5588ee3c551..fb47625a89ac5d39ee49b868795c0b0aaddd27a7 100644 (file)
@@ -53,7 +53,7 @@ class TokenStreamTest extends TestCase
 
     public function testFailToGetNextIdentifier()
     {
-        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
+        $this->expectException('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
 
         $stream = new TokenStream();
         $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2));
@@ -73,7 +73,7 @@ class TokenStreamTest extends TestCase
 
     public function testFailToGetNextIdentifierOrStar()
     {
-        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
+        $this->expectException('Symfony\Component\CssSelector\Exception\SyntaxErrorException');
 
         $stream = new TokenStream();
         $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2));
index a9ab29e2ad622a1245a2cbf1b14656dff3bf5fbd..a718fbd6f2e4cdbe14791faa839fd1b3b91b72ec 100644 (file)
@@ -35,31 +35,25 @@ class TranslatorTest extends TestCase
         $this->assertEquals($xpath, $translator->cssToXPath($css, ''));
     }
 
-    /**
-     * @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
-     */
     public function testCssToXPathPseudoElement()
     {
+        $this->expectException('Symfony\Component\CssSelector\Exception\ExpressionErrorException');
         $translator = new Translator();
         $translator->registerExtension(new HtmlExtension($translator));
         $translator->cssToXPath('e::first-line');
     }
 
-    /**
-     * @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
-     */
     public function testGetExtensionNotExistsExtension()
     {
+        $this->expectException('Symfony\Component\CssSelector\Exception\ExpressionErrorException');
         $translator = new Translator();
         $translator->registerExtension(new HtmlExtension($translator));
         $translator->getExtension('fake');
     }
 
-    /**
-     * @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
-     */
     public function testAddCombinationNotExistsExtension()
     {
+        $this->expectException('Symfony\Component\CssSelector\Exception\ExpressionErrorException');
         $translator = new Translator();
         $translator->registerExtension(new HtmlExtension($translator));
         $parser = new Parser();
@@ -68,11 +62,9 @@ class TranslatorTest extends TestCase
         $translator->addCombination('fake', $xpath, $combinedXpath);
     }
 
-    /**
-     * @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
-     */
     public function testAddFunctionNotExistsFunction()
     {
+        $this->expectException('Symfony\Component\CssSelector\Exception\ExpressionErrorException');
         $translator = new Translator();
         $translator->registerExtension(new HtmlExtension($translator));
         $xpath = new XPathExpr();
@@ -80,22 +72,18 @@ class TranslatorTest extends TestCase
         $translator->addFunction($xpath, $function);
     }
 
-    /**
-     * @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
-     */
     public function testAddPseudoClassNotExistsClass()
     {
+        $this->expectException('Symfony\Component\CssSelector\Exception\ExpressionErrorException');
         $translator = new Translator();
         $translator->registerExtension(new HtmlExtension($translator));
         $xpath = new XPathExpr();
         $translator->addPseudoClass($xpath, 'fake');
     }
 
-    /**
-     * @expectedException \Symfony\Component\CssSelector\Exception\ExpressionErrorException
-     */
     public function testAddAttributeMatchingClassNotExistsClass()
     {
+        $this->expectException('Symfony\Component\CssSelector\Exception\ExpressionErrorException');
         $translator = new Translator();
         $translator->registerExtension(new HtmlExtension($translator));
         $xpath = new XPathExpr();
@@ -110,7 +98,7 @@ class TranslatorTest extends TestCase
         $elements = $document->xpath($translator->cssToXPath($css));
         $this->assertCount(\count($elementsId), $elements);
         foreach ($elements as $element) {
-            $this->assertTrue(\in_array($element->attributes()->id, $elementsId));
+            $this->assertContains((string) $element->attributes()->id, $elementsId);
         }
     }
 
@@ -128,7 +116,7 @@ class TranslatorTest extends TestCase
         $this->assertCount(\count($elementsId), $elementsId);
         foreach ($elements as $element) {
             if (null !== $element->attributes()->id) {
-                $this->assertTrue(\in_array($element->attributes()->id, $elementsId));
+                $this->assertContains((string) $element->attributes()->id, $elementsId);
             }
         }
         libxml_clear_errors();
@@ -149,6 +137,33 @@ class TranslatorTest extends TestCase
         $this->assertCount($count, $elements);
     }
 
+    public function testOnlyOfTypeFindsSingleChildrenOfGivenType()
+    {
+        $translator = new Translator();
+        $translator->registerExtension(new HtmlExtension($translator));
+        $document = new \DOMDocument();
+        $document->loadHTML(<<<'HTML'
+<html>
+  <body>
+    <p>
+      <span>A</span>
+    </p>
+    <p>
+      <span>B</span>
+      <span>C</span>
+    </p>
+  </body>
+</html>
+HTML
+);
+
+        $xpath = new \DOMXPath($document);
+        $nodeList = $xpath->query($translator->cssToXPath('span:only-of-type'));
+
+        $this->assertSame(1, $nodeList->length);
+        $this->assertSame('A', $nodeList->item(0)->textContent);
+    }
+
     public function getXpathLiteralTestData()
     {
         return [
@@ -187,7 +202,7 @@ class TranslatorTest extends TestCase
             ['e:first-of-type', '*/e[position() = 1]'],
             ['e:last-of-type', '*/e[position() = last()]'],
             ['e:only-child', "*/*[(name() = 'e') and (last() = 1)]"],
-            ['e:only-of-type', 'e[last() = 1]'],
+            ['e:only-of-type', 'e[count(preceding-sibling::e)=0 and count(following-sibling::e)=0]'],
             ['e:empty', 'e[not(*) and not(string-length())]'],
             ['e:EmPTY', 'e[not(*) and not(string-length())]'],
             ['e:root', 'e[not(parent::*)]'],
index 0571b3b7651ed0084d751644ca409d1c8ed406f8..f27878b1454a4e193f71f06610801c403b9c7b9a 100644 (file)
@@ -44,9 +44,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
@@ -56,9 +55,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
@@ -68,9 +66,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
@@ -84,9 +81,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
@@ -101,9 +97,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
@@ -117,9 +112,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
@@ -134,9 +128,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
@@ -150,9 +143,8 @@ class AttributeMatchingExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      */
index a8e0b69f1276690e0a8e9e0874e5d52c316143e5..2b8aa6a192a77a935263c5a15465b5eba0e8f13c 100644 (file)
@@ -46,10 +46,8 @@ class FunctionExtension extends AbstractExtension
     }
 
     /**
-     * @param XPathExpr    $xpath
-     * @param FunctionNode $function
-     * @param bool         $last
-     * @param bool         $addNameTest
+     * @param bool $last
+     * @param bool $addNameTest
      *
      * @return XPathExpr
      *
index 27fe47f9a56fb3a926ae57cc8d48da09f2957db4..288a9e695e77b39684102152cf3c1351715a4f9b 100644 (file)
@@ -123,11 +123,13 @@ class PseudoClassExtension extends AbstractExtension
      */
     public function translateOnlyOfType(XPathExpr $xpath)
     {
-        if ('*' === $xpath->getElement()) {
+        $element = $xpath->getElement();
+
+        if ('*' === $element) {
             throw new ExpressionErrorException('"*:only-of-type" is not implemented.');
         }
 
-        return $xpath->addCondition('last() = 1');
+        return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
     }
 
     /**
index d078126772e92b58bc8318de2ccbd4e7a1b8a3f4..7388860310f0e213af61c69594e632b4dbf1a82b 100644 (file)
@@ -180,9 +180,7 @@ class Translator implements TranslatorInterface
     }
 
     /**
-     * @param string        $combiner
-     * @param NodeInterface $xpath
-     * @param NodeInterface $combinedXpath
+     * @param string $combiner
      *
      * @return XPathExpr
      *
@@ -212,8 +210,7 @@ class Translator implements TranslatorInterface
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $pseudoClass
+     * @param string $pseudoClass
      *
      * @return XPathExpr
      *
@@ -229,10 +226,9 @@ class Translator implements TranslatorInterface
     }
 
     /**
-     * @param XPathExpr $xpath
-     * @param string    $operator
-     * @param string    $attribute
-     * @param string    $value
+     * @param string $operator
+     * @param string $attribute
+     * @param string $value
      *
      * @return XPathExpr
      *
index 0b5de83d5712487a6fdb7b8b403fe1e4314cf3a4..fdbdf22555d967f09364be405e215f24a601e3ce 100644 (file)
@@ -38,8 +38,7 @@ interface TranslatorInterface
     /**
      * Translates a parsed selector node to an XPath expression.
      *
-     * @param SelectorNode $selector
-     * @param string       $prefix
+     * @param string $prefix
      *
      * @return string
      */