diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:31:45 +0000 |
commit | 4e393913a4b1f06509da4341f0f58a41adac9117 (patch) | |
tree | 9c27e3eb77d109dff5fd031502311c5616adab04 /vendor/sabberworm | |
parent | Initial commit. (diff) | |
download | icinga-php-thirdparty-4e393913a4b1f06509da4341f0f58a41adac9117.tar.xz icinga-php-thirdparty-4e393913a4b1f06509da4341f0f58a41adac9117.zip |
Adding upstream version 0.12.1+ds.upstream/0.12.1+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/sabberworm')
43 files changed, 6781 insertions, 0 deletions
diff --git a/vendor/sabberworm/php-css-parser/CHANGELOG.md b/vendor/sabberworm/php-css-parser/CHANGELOG.md new file mode 100644 index 0000000..a23fd0e --- /dev/null +++ b/vendor/sabberworm/php-css-parser/CHANGELOG.md @@ -0,0 +1,241 @@ +# Revision History + +## 8.4.0 + +### Features + +* Support for PHP 8.x +* PHPDoc annotations +* Allow usage of CSS variables inside color functions (by parsing them as regular functions) +* Use PSR-12 code style +* *No deprecations* + +### Bugfixes + +* Improved handling of whitespace in `calc()` +* Fix parsing units whose prefix is also a valid unit, like `vmin` +* Allow passing an object to `CSSList#replace` +* Fix PHP 7.3 warnings +* Correctly parse keyframes with `%` +* Don’t convert large numbers to scientific notation +* Allow a file to end after an `@import` +* Preserve case of CSS variables as specced +* Allow identifiers to use escapes the same way as strings +* No longer use `eval` for the comparison in `getSelectorsBySpecificity`, in case it gets passed untrusted input (CVE-2020-13756). Also fixed in 8.3.1, 8.2.1, 8.1.1, 8.0.1, 7.0.4, 6.0.2, 5.2.1, 5.1.3, 5.0.9, 4.0.1, 3.0.1, 2.0.1, 1.0.1. +* Prevent an infinite loop when parsing invalid grid line names +* Remove invalid unit `vm` +* Retain rule order after expanding shorthands + +### Backwards-incompatible changes + +* PHP ≥ 5.6 is now required +* HHVM compatibility target dropped + +## 8.3.0 (2019-02-22) + +* Refactor parsing logic to mostly reside in the class files whose data structure is to be parsed (this should eventually allow us to unit-test specific parts of the parsing logic individually). +* Fix error in parsing `calc` expessions when the first operand is a negative number, thanks to @raxbg. +* Support parsing CSS4 colors in hex notation with alpha values, thanks to @raxbg. +* Swallow more errors in lenient mode, thanks to @raxbg. +* Allow specifying arbitrary strings to output before and after declaration blocks, thanks to @westonruter. +* *No backwards-incompatible changes* +* *No deprecations* + +## 8.2.0 (2018-07-13) + +* Support parsing `calc()`, thanks to @raxbg. +* Support parsing grid-lines, again thanks to @raxbg. +* Support parsing legacy IE filters (`progid:`) in lenient mode, thanks to @FMCorz +* Performance improvements parsing large files, again thanks to @FMCorz +* *No backwards-incompatible changes* +* *No deprecations* + +## 8.1.0 (2016-07-19) + +* Comments are no longer silently ignored but stored with the object with which they appear (no render support, though). Thanks to @FMCorz. +* The IE hacks using `\0` and `\9` can now be parsed (and rendered) in lenient mode. Thanks (again) to @FMCorz. +* Media queries with or without spaces before the query are parsed. Still no *real* parsing support, though. Sorry… +* PHPUnit is now listed as a dev-dependency in composer.json. +* *No backwards-incompatible changes* +* *No deprecations* + +## 8.0.0 (2016-06-30) + +* Store source CSS line numbers in tokens and parsing exceptions. +* *No deprecations* + +### Backwards-incompatible changes + +* Unrecoverable parser errors throw an exception of type `Sabberworm\CSS\Parsing\SourceException` instead of `\Exception`. + +## 7.0.3 (2016-04-27) + +* Fixed parsing empty CSS when multibyte is off +* *No backwards-incompatible changes* +* *No deprecations* + +## 7.0.2 (2016-02-11) + +* 150 time performance boost thanks to @[ossinkine](https://github.com/ossinkine) +* *No backwards-incompatible changes* +* *No deprecations* + +## 7.0.1 (2015-12-25) + +* No more suppressed `E_NOTICE` +* *No backwards-incompatible changes* +* *No deprecations* + +## 7.0.0 (2015-08-24) + +* Compatibility with PHP 7. Well timed, eh? +* *No deprecations* + +### Backwards-incompatible changes + +* The `Sabberworm\CSS\Value\String` class has been renamed to `Sabberworm\CSS\Value\CSSString`. + +## 6.0.1 (2015-08-24) + +* Remove some declarations in interfaces incompatible with PHP 5.3 (< 5.3.9) +* *No deprecations* + +## 6.0.0 (2014-07-03) + +* Format output using Sabberworm\CSS\OutputFormat +* *No backwards-incompatible changes* + +### Deprecations + +* The parse() method replaces __toString with an optional argument (instance of the OutputFormat class) + +## 5.2.0 (2014-06-30) + +* Support removing a selector from a declaration block using `$oBlock->removeSelector($mSelector)` +* Introduce a specialized exception (Sabberworm\CSS\Parsing\OuputException) for exceptions during output rendering + +* *No deprecations* + +#### Backwards-incompatible changes + +* Outputting a declaration block that has no selectors throws an OuputException instead of outputting an invalid ` {…}` into the CSS document. + +## 5.1.2 (2013-10-30) + +* Remove the use of consumeUntil in comment parsing. This makes it possible to parse comments such as `/** Perfectly valid **/` +* Add fr relative size unit +* Fix some issues with HHVM +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.1.1 (2013-10-28) + +* Updated CHANGELOG.md to reflect changes since 5.0.4 +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.1.0 (2013-10-24) + +* Performance enhancements by Michael M Slusarz +* More rescue entry points for lenient parsing (unexpected tokens between declaration blocks and unclosed comments) +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.8 (2013-08-15) + +* Make default settings’ multibyte parsing option dependent on whether or not the mbstring extension is actually installed. +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.7 (2013-08-04) + +* Fix broken decimal point output optimization +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.6 (2013-05-31) + +* Fix broken unit test +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.5 (2013-04-17) + +* Initial support for lenient parsing (setting this parser option will catch some exceptions internally and recover the parser’s state as neatly as possible). +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.4 (2013-03-21) + +* Don’t output floats with locale-aware separator chars +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.3 (2013-03-21) + +* More size units recognized +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.2 (2013-03-21) + +* CHANGELOG.md file added to distribution +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.1 (2013-03-20) + +* Internal cleanup +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.0 (2013-03-20) + +* Correctly parse all known CSS 3 units (including Hz and kHz). +* Output RGB colors in short (#aaa or #ababab) notation +* Be case-insensitive when parsing identifiers. +* *No deprecations* + +### Backwards-incompatible changes + +* `Sabberworm\CSS\Value\Color`’s `__toString` method overrides `CSSList`’s to maybe return something other than `type(value, …)` (see above). + +## 4.0.0 (2013-03-19) + +* Support for more @-rules +* Generic interface `Sabberworm\CSS\Property\AtRule`, implemented by all @-rule classes +* *No deprecations* + +### Backwards-incompatible changes + +* `Sabberworm\CSS\RuleSet\AtRule` renamed to `Sabberworm\CSS\RuleSet\AtRuleSet` +* `Sabberworm\CSS\CSSList\MediaQuery` renamed to `Sabberworm\CSS\RuleSet\CSSList\AtRuleBlockList` with differing semantics and API (which also works for other block-list-based @-rules like `@supports`). + +## 3.0.0 (2013-03-06) + +* Support for lenient parsing (on by default) +* *No deprecations* + +### Backwards-incompatible changes + +* All properties (like whether or not to use `mb_`-functions, which default charset to use and – new – whether or not to be forgiving when parsing) are now encapsulated in an instance of `Sabberworm\CSS\Settings` which can be passed as the second argument to `Sabberworm\CSS\Parser->__construct()`. +* Specifying a charset as the second argument to `Sabberworm\CSS\Parser->__construct()` is no longer supported. Use `Sabberworm\CSS\Settings::create()->withDefaultCharset('some-charset')` instead. +* Setting `Sabberworm\CSS\Parser->bUseMbFunctions` has no effect. Use `Sabberworm\CSS\Settings::create()->withMultibyteSupport(true/false)` instead. +* `Sabberworm\CSS\Parser->parse()` may throw a `Sabberworm\CSS\Parsing\UnexpectedTokenException` when in strict parsing mode. + +## 2.0.0 (2013-01-29) + +* Allow multiple rules of the same type per rule set + +### Backwards-incompatible changes + +* `Sabberworm\CSS\RuleSet->getRules()` returns an index-based array instead of an associative array. Use `Sabberworm\CSS\RuleSet->getRulesAssoc()` (which eliminates duplicate rules and lets the later rule of the same name win). +* `Sabberworm\CSS\RuleSet->removeRule()` works as it did before except when passed an instance of `Sabberworm\CSS\Rule\Rule`, in which case it would only remove the exact rule given instead of all the rules of the same type. To get the old behaviour, use `Sabberworm\CSS\RuleSet->removeRule($oRule->getRule()`; + +## 1.0 + +Initial release of a stable public API. + +## 0.9 + +Last version not to use PSR-0 project organization semantics. diff --git a/vendor/sabberworm/php-css-parser/LICENSE b/vendor/sabberworm/php-css-parser/LICENSE new file mode 100644 index 0000000..686a4e3 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2011 Raphael Schweikert, https://www.sabberworm.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/sabberworm/php-css-parser/README.md b/vendor/sabberworm/php-css-parser/README.md new file mode 100644 index 0000000..66fb1c6 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/README.md @@ -0,0 +1,632 @@ +# PHP CSS Parser + +[![Build Status](https://github.com/sabberworm/PHP-CSS-Parser/workflows/CI/badge.svg?branch=master)](https://github.com/sabberworm/PHP-CSS-Parser/actions/) + +A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output as (optimized) CSS. + +## Usage + +### Installation using Composer + +```bash +composer require sabberworm/php-css-parser +``` + +### Extraction + +To use the CSS Parser, create a new instance. The constructor takes the following form: + +```php +new \Sabberworm\CSS\Parser($css); +``` + +To read a file, for example, you’d do the following: + +```php +$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); +$cssDocument = $parser->parse(); +``` + +The resulting CSS document structure can be manipulated prior to being output. + +### Options + +#### Charset + +The charset option is used only if no `@charset` declaration is found in the CSS file. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that. + +```php +$settings = \Sabberworm\CSS\Settings::create() + ->withDefaultCharset('windows-1252'); +$parser = new \Sabberworm\CSS\Parser($css, $settings); +``` + +#### Strict parsing + +To have the parser choke on invalid rules, supply a thusly configured `\Sabberworm\CSS\Settings` object: + +```php +$parser = new \Sabberworm\CSS\Parser( + file_get_contents('somefile.css'), + \Sabberworm\CSS\Settings::create()->beStrict() +); +``` + +#### Disable multibyte functions + +To achieve faster parsing, you can choose to have PHP-CSS-Parser use regular string functions instead of `mb_*` functions. This should work fine in most cases, even for UTF-8 files, as all the multibyte characters are in string literals. Still it’s not recommended using this with input you have no control over as it’s not thoroughly covered by test cases. + +```php +$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false); +$parser = new \Sabberworm\CSS\Parser($css, $settings); +``` + +### Manipulation + +The resulting data structure consists mainly of five basic types: `CSSList`, `RuleSet`, `Rule`, `Selector` and `Value`. There are two additional types used: `Import` and `Charset`, which you won’t use often. + +#### CSSList + +`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc. `CSSList` has the following concrete subtypes: + +* `Document` – representing the root of a CSS file. +* `MediaQuery` – represents a subsection of a `CSSList` that only applies to an output device matching the contained media query. + +To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use instanceof to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`. + +To append a new item (selector, media query, etc.) to an existing `CSSList`, construct it using the constructor for this class and use the `append($oItem)` method. + +#### RuleSet + +`RuleSet` is a container for individual rules. The most common form of a rule set is one constrained by a selector. The following concrete subtypes exist: + +* `AtRuleSet` – for generic at-rules which do not match the ones specifically mentioned like `@import`, `@charset` or `@media`. A common example for this is `@font-face`. +* `DeclarationBlock` – a `RuleSet` constrained by a `Selector`; contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the matching elements. + +Note: A `CSSList` can contain other `CSSList`s (and `Import`s as well as a `Charset`), while a `RuleSet` can only contain `Rule`s. + +If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` instance or a rule name; optionally suffixed by a dash to remove all related rules). + +#### Rule + +`Rule`s just have a key (the rule) and a value. These values are all instances of a `Value`. + +#### Value + +`Value` is an abstract class that only defines the `render` method. The concrete subclasses for atomic value types are: + +* `Size` – consists of a numeric `size` value and a unit. +* `Color` – colors can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form. +* `CSSString` – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes. +* `URL` – URLs in CSS; always output in URL("") notation. + +There is another abstract subclass of `Value`, `ValueList`. A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`). There are two types of `ValueList`s: + +* `RuleValueList` – The default type, used to represent all multi-valued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list). +* `CSSFunction` – A special kind of value that also contains a function name and where the values are the function’s arguments. Also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`. + +#### Convenience methods + +There are a few convenience methods on Document to ease finding, manipulating and deleting rules: + +* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested your selectors are. Aliased as `getAllSelectors()`. +* `getAllRuleSets()` – does what it says; no matter how deeply nested your rule sets are. +* `getAllValues()` – finds all `Value` objects inside `Rule`s. + +## To-Do + +* More convenience methods (like `selectorsWithElement($sId/Class/TagName)`, `attributesOfType($type)`, `removeAttributesOfType($type)`) +* Real multibyte support. Currently, only multibyte charsets whose first 255 code points take up only one byte and are identical with ASCII are supported (yes, UTF-8 fits this description). +* Named color support (using `Color` instead of an anonymous string literal) + +## Use cases + +### Use `Parser` to prepend an ID to all selectors + +```php +$myId = "#my_id"; +$parser = new \Sabberworm\CSS\Parser($css); +$cssDocument = $parser->parse(); +foreach ($cssDocument->getAllDeclarationBlocks() as $block) { + foreach ($block->getSelectors() as $selector) { + // Loop over all selector parts (the comma-separated strings in a + // selector) and prepend the ID. + $selector->setSelector($myId.' '.$selector->getSelector()); + } +} +``` + +### Shrink all absolute sizes to half + +```php +$parser = new \Sabberworm\CSS\Parser($css); +$cssDocument = $parser->parse(); +foreach ($cssDocument->getAllValues() as $value) { + if ($value instanceof CSSSize && !$value->isRelative()) { + $value->setSize($value->getSize() / 2); + } +} +``` + +### Remove unwanted rules + +```php +$parser = new \Sabberworm\CSS\Parser($css); +$cssDocument = $parser->parse(); +foreach($cssDocument->getAllRuleSets() as $oRuleSet) { + // Note that the added dash will make this remove all rules starting with + // `font-` (like `font-size`, `font-weight`, etc.) as well as a potential + // `font-rule`. + $oRuleSet->removeRule('font-'); + $oRuleSet->removeRule('cursor'); +} +``` + +### Output + +To output the entire CSS document into a variable, just use `->render()`: + +```php +$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); +$cssDocument = $parser->parse(); +print $cssDocument->render(); +``` + +If you want to format the output, pass an instance of type `\Sabberworm\CSS\OutputFormat`: + +```php +$format = \Sabberworm\CSS\OutputFormat::create() + ->indentWithSpaces(4)->setSpaceBetweenRules("\n"); +print $cssDocument->render($format); +``` + +Or use one of the predefined formats: + +```php +print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty()); +print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact()); +``` + +To see what you can do with output formatting, look at the tests in `tests/OutputFormatTest.php`. + +## Examples + +### Example 1 (At-Rules) + +#### Input + +```css +@charset "utf-8"; + +@font-face { + font-family: "CrassRoots"; + src: url("../media/cr.ttf"); +} + +html, body { + font-size: 1.6em; +} + +@keyframes mymove { + from { top: 0px; } + to { top: 200px; } +} + +``` + +#### Structure (`var_dump()`) + +```php +class Sabberworm\CSS\CSSList\Document#4 (2) { + protected $aContents => + array(4) { + [0] => + class Sabberworm\CSS\Property\Charset#6 (2) { + private $sCharset => + class Sabberworm\CSS\Value\CSSString#5 (2) { + private $sString => + string(5) "utf-8" + protected $iLineNo => + int(1) + } + protected $iLineNo => + int(1) + } + [1] => + class Sabberworm\CSS\RuleSet\AtRuleSet#7 (4) { + private $sType => + string(9) "font-face" + private $sArgs => + string(0) "" + private $aRules => + array(2) { + 'font-family' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#8 (4) { + private $sRule => + string(11) "font-family" + private $mValue => + class Sabberworm\CSS\Value\CSSString#9 (2) { + private $sString => + string(10) "CrassRoots" + protected $iLineNo => + int(4) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(4) + } + } + 'src' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#10 (4) { + private $sRule => + string(3) "src" + private $mValue => + class Sabberworm\CSS\Value\URL#11 (2) { + private $oURL => + class Sabberworm\CSS\Value\CSSString#12 (2) { + private $sString => + string(15) "../media/cr.ttf" + protected $iLineNo => + int(5) + } + protected $iLineNo => + int(5) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(5) + } + } + } + protected $iLineNo => + int(3) + } + [2] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#13 (3) { + private $aSelectors => + array(2) { + [0] => + class Sabberworm\CSS\Property\Selector#14 (2) { + private $sSelector => + string(4) "html" + private $iSpecificity => + NULL + } + [1] => + class Sabberworm\CSS\Property\Selector#15 (2) { + private $sSelector => + string(4) "body" + private $iSpecificity => + NULL + } + } + private $aRules => + array(1) { + 'font-size' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#16 (4) { + private $sRule => + string(9) "font-size" + private $mValue => + class Sabberworm\CSS\Value\Size#17 (4) { + private $fSize => + double(1.6) + private $sUnit => + string(2) "em" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(9) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(9) + } + } + } + protected $iLineNo => + int(8) + } + [3] => + class Sabberworm\CSS\CSSList\KeyFrame#18 (4) { + private $vendorKeyFrame => + string(9) "keyframes" + private $animationName => + string(6) "mymove" + protected $aContents => + array(2) { + [0] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#19 (3) { + private $aSelectors => + array(1) { + [0] => + class Sabberworm\CSS\Property\Selector#20 (2) { + private $sSelector => + string(4) "from" + private $iSpecificity => + NULL + } + } + private $aRules => + array(1) { + 'top' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#21 (4) { + private $sRule => + string(3) "top" + private $mValue => + class Sabberworm\CSS\Value\Size#22 (4) { + private $fSize => + double(0) + private $sUnit => + string(2) "px" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(13) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(13) + } + } + } + protected $iLineNo => + int(13) + } + [1] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#23 (3) { + private $aSelectors => + array(1) { + [0] => + class Sabberworm\CSS\Property\Selector#24 (2) { + private $sSelector => + string(2) "to" + private $iSpecificity => + NULL + } + } + private $aRules => + array(1) { + 'top' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#25 (4) { + private $sRule => + string(3) "top" + private $mValue => + class Sabberworm\CSS\Value\Size#26 (4) { + private $fSize => + double(200) + private $sUnit => + string(2) "px" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(14) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(14) + } + } + } + protected $iLineNo => + int(14) + } + } + protected $iLineNo => + int(12) + } + } + protected $iLineNo => + int(1) +} + +``` + +#### Output (`render()`) + +```css +@charset "utf-8"; +@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");} +html, body {font-size: 1.6em;} +@keyframes mymove {from {top: 0px;} to {top: 200px;}} +``` + +### Example 2 (Values) + +#### Input + +```css +#header { + margin: 10px 2em 1cm 2%; + font-family: Verdana, Helvetica, "Gill Sans", sans-serif; + color: red !important; +} + +``` + +#### Structure (`var_dump()`) + +```php +class Sabberworm\CSS\CSSList\Document#4 (2) { + protected $aContents => + array(1) { + [0] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#5 (3) { + private $aSelectors => + array(1) { + [0] => + class Sabberworm\CSS\Property\Selector#6 (2) { + private $sSelector => + string(7) "#header" + private $iSpecificity => + NULL + } + } + private $aRules => + array(3) { + 'margin' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#7 (4) { + private $sRule => + string(6) "margin" + private $mValue => + class Sabberworm\CSS\Value\RuleValueList#12 (3) { + protected $aComponents => + array(4) { + [0] => + class Sabberworm\CSS\Value\Size#8 (4) { + private $fSize => + double(10) + private $sUnit => + string(2) "px" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + [1] => + class Sabberworm\CSS\Value\Size#9 (4) { + private $fSize => + double(2) + private $sUnit => + string(2) "em" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + [2] => + class Sabberworm\CSS\Value\Size#10 (4) { + private $fSize => + double(1) + private $sUnit => + string(2) "cm" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + [3] => + class Sabberworm\CSS\Value\Size#11 (4) { + private $fSize => + double(2) + private $sUnit => + string(1) "%" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + } + protected $sSeparator => + string(1) " " + protected $iLineNo => + int(2) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(2) + } + } + 'font-family' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#13 (4) { + private $sRule => + string(11) "font-family" + private $mValue => + class Sabberworm\CSS\Value\RuleValueList#15 (3) { + protected $aComponents => + array(4) { + [0] => + string(7) "Verdana" + [1] => + string(9) "Helvetica" + [2] => + class Sabberworm\CSS\Value\CSSString#14 (2) { + private $sString => + string(9) "Gill Sans" + protected $iLineNo => + int(3) + } + [3] => + string(10) "sans-serif" + } + protected $sSeparator => + string(1) "," + protected $iLineNo => + int(3) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(3) + } + } + 'color' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#16 (4) { + private $sRule => + string(5) "color" + private $mValue => + string(3) "red" + private $bIsImportant => + bool(true) + protected $iLineNo => + int(4) + } + } + } + protected $iLineNo => + int(1) + } + } + protected $iLineNo => + int(1) +} + +``` + +#### Output (`render()`) + +```css +#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;} +``` + +## Contributors/Thanks to + +* [oliverklee](https://github.com/oliverklee) for lots of refactorings, code modernizations and CI integrations +* [raxbg](https://github.com/raxbg) for contributions to parse `calc`, grid lines, and various bugfixes. +* [westonruter](https://github.com/westonruter) for bugfixes and improvements. +* [FMCorz](https://github.com/FMCorz) for many patches and suggestions, for being able to parse comments and IE hacks (in lenient mode). +* [Lullabot](https://github.com/Lullabot) for a patch that allows to know the line number for each parsed token. +* [ju1ius](https://github.com/ju1ius) for the specificity parsing code and the ability to expand/compact shorthand properties. +* [ossinkine](https://github.com/ossinkine) for a 150 time performance boost. +* [GaryJones](https://github.com/GaryJones) for lots of input and [https://css-specificity.info/](https://css-specificity.info/). +* [docteurklein](https://github.com/docteurklein) for output formatting and `CSSList->remove()` inspiration. +* [nicolopignatelli](https://github.com/nicolopignatelli) for PSR-0 compatibility. +* [diegoembarcadero](https://github.com/diegoembarcadero) for keyframe at-rule parsing. +* [goetas](https://github.com/goetas) for @namespace at-rule support. +* [View full list](https://github.com/sabberworm/PHP-CSS-Parser/contributors) + +## Misc + +* Legacy Support: The latest pre-PSR-0 version of this project can be checked with the `0.9.0` tag. +* Running Tests: To run all unit tests for this project, run `composer install` to install phpunit and use `./vendor/bin/phpunit`. diff --git a/vendor/sabberworm/php-css-parser/composer.json b/vendor/sabberworm/php-css-parser/composer.json new file mode 100644 index 0000000..e192dd5 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/composer.json @@ -0,0 +1,69 @@ +{ + "name": "sabberworm/php-css-parser", + "type": "library", + "description": "Parser for CSS Files written in PHP", + "keywords": [ + "parser", + "css", + "stylesheet" + ], + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "license": "MIT", + "authors": [ + { + "name": "Raphael Schweikert" + } + ], + "require": { + "php": ">=5.6.20", + "ext-iconv": "*" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36", + "codacy/coverage": "^1.4" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Sabberworm\\CSS\\Tests\\": "tests/" + } + }, + "scripts": { + "ci": [ + "@ci:static" + ], + "ci:php:fixer": "@php ./.phive/php-cs-fixer.phar --config=config/php-cs-fixer.php fix --dry-run -v --show-progress=dots bin src tests", + "ci:php:sniffer": "@php ./.phive/phpcs.phar --standard=config/phpcs.xml bin src tests", + "ci:php:stan": "@php ./.phive/phpstan.phar --configuration=config/phpstan.neon", + "ci:static": [ + "@ci:php:fixer", + "@ci:php:sniffer", + "@ci:php:stan" + ], + "fix:php": [ + "@fix:php:fixer", + "@fix:php:sniffer" + ], + "fix:php:fixer": "@php ./.phive/php-cs-fixer.phar --config=config/php-cs-fixer.php fix bin src tests", + "fix:php:sniffer": "@php ./.phive/phpcbf.phar --standard=config/phpcs.xml bin src tests", + "phpstan:baseline": "@php ./.phive/phpstan.phar --configuration=config/phpstan.neon --generate-baseline=config/phpstan-baseline.neon" + }, + "scripts-descriptions": { + "ci": "Runs all dynamic and static code checks (i.e. currently, only the static checks).", + "ci:php:fixer": "Checks the code style with PHP CS Fixer.", + "ci:php:sniffer": "Checks the code style with PHP_CodeSniffer.", + "ci:php:stan": "Checks the types with PHPStan.", + "ci:static": "Runs all static code analysis checks for the code.", + "fix:php": "Autofixes all autofixable issues in the PHP code.", + "fix:php:fixer": "Fixes autofixable issues found by PHP CS Fixer.", + "fix:php:sniffer": "Fixes autofixable issues found by PHP_CodeSniffer.", + "phpstand:baseline": "Updates the PHPStan baseline file to match the code." + } +} diff --git a/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php b/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php new file mode 100644 index 0000000..218adb9 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php @@ -0,0 +1,83 @@ +<?php + +namespace Sabberworm\CSS\CSSList; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Property\AtRule; + +/** + * A `BlockList` constructed by an unknown at-rule. `@media` rules are rendered into `AtRuleBlockList` objects. + */ +class AtRuleBlockList extends CSSBlockList implements AtRule +{ + /** + * @var string + */ + private $sType; + + /** + * @var string + */ + private $sArgs; + + /** + * @param string $sType + * @param string $sArgs + * @param int $iLineNo + */ + public function __construct($sType, $sArgs = '', $iLineNo = 0) + { + parent::__construct($iLineNo); + $this->sType = $sType; + $this->sArgs = $sArgs; + } + + /** + * @return string + */ + public function atRuleName() + { + return $this->sType; + } + + /** + * @return string + */ + public function atRuleArgs() + { + return $this->sArgs; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sArgs = $this->sArgs; + if ($sArgs) { + $sArgs = ' ' . $sArgs; + } + $sResult = $oOutputFormat->sBeforeAtRuleBlock; + $sResult .= "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{"; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + $sResult .= $oOutputFormat->sAfterAtRuleBlock; + return $sResult; + } + + /** + * @return bool + */ + public function isRootList() + { + return false; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php b/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php new file mode 100644 index 0000000..fce7913 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php @@ -0,0 +1,143 @@ +<?php + +namespace Sabberworm\CSS\CSSList; + +use Sabberworm\CSS\Property\Selector; +use Sabberworm\CSS\Rule\Rule; +use Sabberworm\CSS\RuleSet\DeclarationBlock; +use Sabberworm\CSS\RuleSet\RuleSet; +use Sabberworm\CSS\Value\CSSFunction; +use Sabberworm\CSS\Value\Value; +use Sabberworm\CSS\Value\ValueList; + +/** + * A `CSSBlockList` is a `CSSList` whose `DeclarationBlock`s are guaranteed to contain valid declaration blocks or + * at-rules. + * + * Most `CSSList`s conform to this category but some at-rules (such as `@keyframes`) do not. + */ +abstract class CSSBlockList extends CSSList +{ + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + parent::__construct($iLineNo); + } + + /** + * @param array<int, DeclarationBlock> $aResult + * + * @return void + */ + protected function allDeclarationBlocks(array &$aResult) + { + foreach ($this->aContents as $mContent) { + if ($mContent instanceof DeclarationBlock) { + $aResult[] = $mContent; + } elseif ($mContent instanceof CSSBlockList) { + $mContent->allDeclarationBlocks($aResult); + } + } + } + + /** + * @param array<int, RuleSet> $aResult + * + * @return void + */ + protected function allRuleSets(array &$aResult) + { + foreach ($this->aContents as $mContent) { + if ($mContent instanceof RuleSet) { + $aResult[] = $mContent; + } elseif ($mContent instanceof CSSBlockList) { + $mContent->allRuleSets($aResult); + } + } + } + + /** + * @param CSSList|Rule|RuleSet|Value $oElement + * @param array<int, Value> $aResult + * @param string|null $sSearchString + * @param bool $bSearchInFunctionArguments + * + * @return void + */ + protected function allValues($oElement, array &$aResult, $sSearchString = null, $bSearchInFunctionArguments = false) + { + if ($oElement instanceof CSSBlockList) { + foreach ($oElement->getContents() as $oContent) { + $this->allValues($oContent, $aResult, $sSearchString, $bSearchInFunctionArguments); + } + } elseif ($oElement instanceof RuleSet) { + foreach ($oElement->getRules($sSearchString) as $oRule) { + $this->allValues($oRule, $aResult, $sSearchString, $bSearchInFunctionArguments); + } + } elseif ($oElement instanceof Rule) { + $this->allValues($oElement->getValue(), $aResult, $sSearchString, $bSearchInFunctionArguments); + } elseif ($oElement instanceof ValueList) { + if ($bSearchInFunctionArguments || !($oElement instanceof CSSFunction)) { + foreach ($oElement->getListComponents() as $mComponent) { + $this->allValues($mComponent, $aResult, $sSearchString, $bSearchInFunctionArguments); + } + } + } else { + // Non-List `Value` or `CSSString` (CSS identifier) + $aResult[] = $oElement; + } + } + + /** + * @param array<int, Selector> $aResult + * @param string|null $sSpecificitySearch + * + * @return void + */ + protected function allSelectors(array &$aResult, $sSpecificitySearch = null) + { + /** @var array<int, DeclarationBlock> $aDeclarationBlocks */ + $aDeclarationBlocks = []; + $this->allDeclarationBlocks($aDeclarationBlocks); + foreach ($aDeclarationBlocks as $oBlock) { + foreach ($oBlock->getSelectors() as $oSelector) { + if ($sSpecificitySearch === null) { + $aResult[] = $oSelector; + } else { + $sComparator = '==='; + $aSpecificitySearch = explode(' ', $sSpecificitySearch); + $iTargetSpecificity = $aSpecificitySearch[0]; + if (count($aSpecificitySearch) > 1) { + $sComparator = $aSpecificitySearch[0]; + $iTargetSpecificity = $aSpecificitySearch[1]; + } + $iTargetSpecificity = (int)$iTargetSpecificity; + $iSelectorSpecificity = $oSelector->getSpecificity(); + $bMatches = false; + switch ($sComparator) { + case '<=': + $bMatches = $iSelectorSpecificity <= $iTargetSpecificity; + break; + case '<': + $bMatches = $iSelectorSpecificity < $iTargetSpecificity; + break; + case '>=': + $bMatches = $iSelectorSpecificity >= $iTargetSpecificity; + break; + case '>': + $bMatches = $iSelectorSpecificity > $iTargetSpecificity; + break; + default: + $bMatches = $iSelectorSpecificity === $iTargetSpecificity; + break; + } + if ($bMatches) { + $aResult[] = $oSelector; + } + } + } + } + } +} diff --git a/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php b/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php new file mode 100644 index 0000000..946740a --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php @@ -0,0 +1,479 @@ +<?php + +namespace Sabberworm\CSS\CSSList; + +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\Comment\Commentable; +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\SourceException; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; +use Sabberworm\CSS\Property\AtRule; +use Sabberworm\CSS\Property\Charset; +use Sabberworm\CSS\Property\CSSNamespace; +use Sabberworm\CSS\Property\Import; +use Sabberworm\CSS\Property\Selector; +use Sabberworm\CSS\Renderable; +use Sabberworm\CSS\RuleSet\AtRuleSet; +use Sabberworm\CSS\RuleSet\DeclarationBlock; +use Sabberworm\CSS\RuleSet\RuleSet; +use Sabberworm\CSS\Settings; +use Sabberworm\CSS\Value\CSSString; +use Sabberworm\CSS\Value\URL; +use Sabberworm\CSS\Value\Value; + +/** + * A `CSSList` is the most generic container available. Its contents include `RuleSet` as well as other `CSSList` + * objects. + * + * Also, it may contain `Import` and `Charset` objects stemming from at-rules. + */ +abstract class CSSList implements Renderable, Commentable +{ + /** + * @var array<array-key, Comment> + */ + protected $aComments; + + /** + * @var array<int, RuleSet|CSSList|Import|Charset> + */ + protected $aContents; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + $this->aComments = []; + $this->aContents = []; + $this->iLineNo = $iLineNo; + } + + /** + * @return void + * + * @throws UnexpectedTokenException + * @throws SourceException + */ + public static function parseList(ParserState $oParserState, CSSList $oList) + { + $bIsRoot = $oList instanceof Document; + if (is_string($oParserState)) { + $oParserState = new ParserState($oParserState, Settings::create()); + } + $bLenientParsing = $oParserState->getSettings()->bLenientParsing; + while (!$oParserState->isEnd()) { + $comments = $oParserState->consumeWhiteSpace(); + $oListItem = null; + if ($bLenientParsing) { + try { + $oListItem = self::parseListItem($oParserState, $oList); + } catch (UnexpectedTokenException $e) { + $oListItem = false; + } + } else { + $oListItem = self::parseListItem($oParserState, $oList); + } + if ($oListItem === null) { + // List parsing finished + return; + } + if ($oListItem) { + $oListItem->setComments($comments); + $oList->append($oListItem); + } + $oParserState->consumeWhiteSpace(); + } + if (!$bIsRoot && !$bLenientParsing) { + throw new SourceException("Unexpected end of document", $oParserState->currentLine()); + } + } + + /** + * @return AtRuleBlockList|KeyFrame|Charset|CSSNamespace|Import|AtRuleSet|DeclarationBlock|null|false + * + * @throws SourceException + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + private static function parseListItem(ParserState $oParserState, CSSList $oList) + { + $bIsRoot = $oList instanceof Document; + if ($oParserState->comes('@')) { + $oAtRule = self::parseAtRule($oParserState); + if ($oAtRule instanceof Charset) { + if (!$bIsRoot) { + throw new UnexpectedTokenException( + '@charset may only occur in root document', + '', + 'custom', + $oParserState->currentLine() + ); + } + if (count($oList->getContents()) > 0) { + throw new UnexpectedTokenException( + '@charset must be the first parseable token in a document', + '', + 'custom', + $oParserState->currentLine() + ); + } + $oParserState->setCharset($oAtRule->getCharset()->getString()); + } + return $oAtRule; + } elseif ($oParserState->comes('}')) { + if (!$oParserState->getSettings()->bLenientParsing) { + throw new UnexpectedTokenException('CSS selector', '}', 'identifier', $oParserState->currentLine()); + } else { + if ($bIsRoot) { + if ($oParserState->getSettings()->bLenientParsing) { + return DeclarationBlock::parse($oParserState); + } else { + throw new SourceException("Unopened {", $oParserState->currentLine()); + } + } else { + return null; + } + } + } else { + return DeclarationBlock::parse($oParserState, $oList); + } + } + + /** + * @param ParserState $oParserState + * + * @return AtRuleBlockList|KeyFrame|Charset|CSSNamespace|Import|AtRuleSet|null + * + * @throws SourceException + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + private static function parseAtRule(ParserState $oParserState) + { + $oParserState->consume('@'); + $sIdentifier = $oParserState->parseIdentifier(); + $iIdentifierLineNum = $oParserState->currentLine(); + $oParserState->consumeWhiteSpace(); + if ($sIdentifier === 'import') { + $oLocation = URL::parse($oParserState); + $oParserState->consumeWhiteSpace(); + $sMediaQuery = null; + if (!$oParserState->comes(';')) { + $sMediaQuery = trim($oParserState->consumeUntil([';', ParserState::EOF])); + } + $oParserState->consumeUntil([';', ParserState::EOF], true, true); + return new Import($oLocation, $sMediaQuery ?: null, $iIdentifierLineNum); + } elseif ($sIdentifier === 'charset') { + $sCharset = CSSString::parse($oParserState); + $oParserState->consumeWhiteSpace(); + $oParserState->consumeUntil([';', ParserState::EOF], true, true); + return new Charset($sCharset, $iIdentifierLineNum); + } elseif (self::identifierIs($sIdentifier, 'keyframes')) { + $oResult = new KeyFrame($iIdentifierLineNum); + $oResult->setVendorKeyFrame($sIdentifier); + $oResult->setAnimationName(trim($oParserState->consumeUntil('{', false, true))); + CSSList::parseList($oParserState, $oResult); + if ($oParserState->comes('}')) { + $oParserState->consume('}'); + } + return $oResult; + } elseif ($sIdentifier === 'namespace') { + $sPrefix = null; + $mUrl = Value::parsePrimitiveValue($oParserState); + if (!$oParserState->comes(';')) { + $sPrefix = $mUrl; + $mUrl = Value::parsePrimitiveValue($oParserState); + } + $oParserState->consumeUntil([';', ParserState::EOF], true, true); + if ($sPrefix !== null && !is_string($sPrefix)) { + throw new UnexpectedTokenException('Wrong namespace prefix', $sPrefix, 'custom', $iIdentifierLineNum); + } + if (!($mUrl instanceof CSSString || $mUrl instanceof URL)) { + throw new UnexpectedTokenException( + 'Wrong namespace url of invalid type', + $mUrl, + 'custom', + $iIdentifierLineNum + ); + } + return new CSSNamespace($mUrl, $sPrefix, $iIdentifierLineNum); + } else { + // Unknown other at rule (font-face or such) + $sArgs = trim($oParserState->consumeUntil('{', false, true)); + if (substr_count($sArgs, "(") != substr_count($sArgs, ")")) { + if ($oParserState->getSettings()->bLenientParsing) { + return null; + } else { + throw new SourceException("Unmatched brace count in media query", $oParserState->currentLine()); + } + } + $bUseRuleSet = true; + foreach (explode('/', AtRule::BLOCK_RULES) as $sBlockRuleName) { + if (self::identifierIs($sIdentifier, $sBlockRuleName)) { + $bUseRuleSet = false; + break; + } + } + if ($bUseRuleSet) { + $oAtRule = new AtRuleSet($sIdentifier, $sArgs, $iIdentifierLineNum); + RuleSet::parseRuleSet($oParserState, $oAtRule); + } else { + $oAtRule = new AtRuleBlockList($sIdentifier, $sArgs, $iIdentifierLineNum); + CSSList::parseList($oParserState, $oAtRule); + if ($oParserState->comes('}')) { + $oParserState->consume('}'); + } + } + return $oAtRule; + } + } + + /** + * Tests an identifier for a given value. Since identifiers are all keywords, they can be vendor-prefixed. + * We need to check for these versions too. + * + * @param string $sIdentifier + * @param string $sMatch + * + * @return bool + */ + private static function identifierIs($sIdentifier, $sMatch) + { + return (strcasecmp($sIdentifier, $sMatch) === 0) + ?: preg_match("/^(-\\w+-)?$sMatch$/i", $sIdentifier) === 1; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * Prepends an item to the list of contents. + * + * @param RuleSet|CSSList|Import|Charset $oItem + * + * @return void + */ + public function prepend($oItem) + { + array_unshift($this->aContents, $oItem); + } + + /** + * Appends an item to tje list of contents. + * + * @param RuleSet|CSSList|Import|Charset $oItem + * + * @return void + */ + public function append($oItem) + { + $this->aContents[] = $oItem; + } + + /** + * Splices the list of contents. + * + * @param int $iOffset + * @param int $iLength + * @param array<int, RuleSet|CSSList|Import|Charset> $mReplacement + * + * @return void + */ + public function splice($iOffset, $iLength = null, $mReplacement = null) + { + array_splice($this->aContents, $iOffset, $iLength, $mReplacement); + } + + /** + * Removes an item from the CSS list. + * + * @param RuleSet|Import|Charset|CSSList $oItemToRemove + * May be a RuleSet (most likely a DeclarationBlock), a Import, + * a Charset or another CSSList (most likely a MediaQuery) + * + * @return bool whether the item was removed + */ + public function remove($oItemToRemove) + { + $iKey = array_search($oItemToRemove, $this->aContents, true); + if ($iKey !== false) { + unset($this->aContents[$iKey]); + return true; + } + return false; + } + + /** + * Replaces an item from the CSS list. + * + * @param RuleSet|Import|Charset|CSSList $oOldItem + * May be a `RuleSet` (most likely a `DeclarationBlock`), an `Import`, a `Charset` + * or another `CSSList` (most likely a `MediaQuery`) + * + * @return bool + */ + public function replace($oOldItem, $mNewItem) + { + $iKey = array_search($oOldItem, $this->aContents, true); + if ($iKey !== false) { + if (is_array($mNewItem)) { + array_splice($this->aContents, $iKey, 1, $mNewItem); + } else { + array_splice($this->aContents, $iKey, 1, [$mNewItem]); + } + return true; + } + return false; + } + + /** + * @param array<int, RuleSet|Import|Charset|CSSList> $aContents + */ + public function setContents(array $aContents) + { + $this->aContents = []; + foreach ($aContents as $content) { + $this->append($content); + } + } + + /** + * Removes a declaration block from the CSS list if it matches all given selectors. + * + * @param DeclarationBlock|array<array-key, Selector>|string $mSelector the selectors to match + * @param bool $bRemoveAll whether to stop at the first declaration block found or remove all blocks + * + * @return void + */ + public function removeDeclarationBlockBySelector($mSelector, $bRemoveAll = false) + { + if ($mSelector instanceof DeclarationBlock) { + $mSelector = $mSelector->getSelectors(); + } + if (!is_array($mSelector)) { + $mSelector = explode(',', $mSelector); + } + foreach ($mSelector as $iKey => &$mSel) { + if (!($mSel instanceof Selector)) { + if (!Selector::isValid($mSel)) { + throw new UnexpectedTokenException( + "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.", + $mSel, + "custom" + ); + } + $mSel = new Selector($mSel); + } + } + foreach ($this->aContents as $iKey => $mItem) { + if (!($mItem instanceof DeclarationBlock)) { + continue; + } + if ($mItem->getSelectors() == $mSelector) { + unset($this->aContents[$iKey]); + if (!$bRemoveAll) { + return; + } + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = ''; + $bIsFirst = true; + $oNextLevel = $oOutputFormat; + if (!$this->isRootList()) { + $oNextLevel = $oOutputFormat->nextLevel(); + } + foreach ($this->aContents as $oContent) { + $sRendered = $oOutputFormat->safely(function () use ($oNextLevel, $oContent) { + return $oContent->render($oNextLevel); + }); + if ($sRendered === null) { + continue; + } + if ($bIsFirst) { + $bIsFirst = false; + $sResult .= $oNextLevel->spaceBeforeBlocks(); + } else { + $sResult .= $oNextLevel->spaceBetweenBlocks(); + } + $sResult .= $sRendered; + } + + if (!$bIsFirst) { + // Had some output + $sResult .= $oOutputFormat->spaceAfterBlocks(); + } + + return $sResult; + } + + /** + * Return true if the list can not be further outdented. Only important when rendering. + * + * @return bool + */ + abstract public function isRootList(); + + /** + * @return array<int, RuleSet|Import|Charset|CSSList> + */ + public function getContents() + { + return $this->aContents; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array<array-key, Comment> + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/CSSList/Document.php b/vendor/sabberworm/php-css-parser/src/CSSList/Document.php new file mode 100644 index 0000000..91ab2c6 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/CSSList/Document.php @@ -0,0 +1,172 @@ +<?php + +namespace Sabberworm\CSS\CSSList; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\SourceException; +use Sabberworm\CSS\Property\Selector; +use Sabberworm\CSS\RuleSet\DeclarationBlock; +use Sabberworm\CSS\RuleSet\RuleSet; +use Sabberworm\CSS\Value\Value; + +/** + * The root `CSSList` of a parsed file. Contains all top-level CSS contents, mostly declaration blocks, + * but also any at-rules encountered. + */ +class Document extends CSSBlockList +{ + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + parent::__construct($iLineNo); + } + + /** + * @return Document + * + * @throws SourceException + */ + public static function parse(ParserState $oParserState) + { + $oDocument = new Document($oParserState->currentLine()); + CSSList::parseList($oParserState, $oDocument); + return $oDocument; + } + + /** + * Gets all `DeclarationBlock` objects recursively. + * + * @return array<int, DeclarationBlock> + */ + public function getAllDeclarationBlocks() + { + /** @var array<int, DeclarationBlock> $aResult */ + $aResult = []; + $this->allDeclarationBlocks($aResult); + return $aResult; + } + + /** + * Gets all `DeclarationBlock` objects recursively. + * + * @return array<int, DeclarationBlock> + * + * @deprecated will be removed in version 9.0; use `getAllDeclarationBlocks()` instead + */ + public function getAllSelectors() + { + return $this->getAllDeclarationBlocks(); + } + + /** + * Returns all `RuleSet` objects found recursively in the tree. + * + * @return array<int, RuleSet> + */ + public function getAllRuleSets() + { + /** @var array<int, RuleSet> $aResult */ + $aResult = []; + $this->allRuleSets($aResult); + return $aResult; + } + + /** + * Returns all `Value` objects found recursively in the tree. + * + * @param CSSList|RuleSet|string $mElement + * the `CSSList` or `RuleSet` to start the search from (defaults to the whole document). + * If a string is given, it is used as rule name filter. + * @param bool $bSearchInFunctionArguments whether to also return Value objects used as Function arguments. + * + * @return array<int, Value> + * + * @see RuleSet->getRules() + */ + public function getAllValues($mElement = null, $bSearchInFunctionArguments = false) + { + $sSearchString = null; + if ($mElement === null) { + $mElement = $this; + } elseif (is_string($mElement)) { + $sSearchString = $mElement; + $mElement = $this; + } + /** @var array<int, Value> $aResult */ + $aResult = []; + $this->allValues($mElement, $aResult, $sSearchString, $bSearchInFunctionArguments); + return $aResult; + } + + /** + * Returns all `Selector` objects found recursively in the tree. + * + * Note that this does not yield the full `DeclarationBlock` that the selector belongs to + * (and, currently, there is no way to get to that). + * + * @param string|null $sSpecificitySearch + * An optional filter by specificity. + * May contain a comparison operator and a number or just a number (defaults to "=="). + * + * @return array<int, Selector> + * @example `getSelectorsBySpecificity('>= 100')` + * + */ + public function getSelectorsBySpecificity($sSpecificitySearch = null) + { + /** @var array<int, Selector> $aResult */ + $aResult = []; + $this->allSelectors($aResult, $sSpecificitySearch); + return $aResult; + } + + /** + * Expands all shorthand properties to their long value. + * + * @return void + */ + public function expandShorthands() + { + foreach ($this->getAllDeclarationBlocks() as $oDeclaration) { + $oDeclaration->expandShorthands(); + } + } + + /** + * Create shorthands properties whenever possible. + * + * @return void + */ + public function createShorthands() + { + foreach ($this->getAllDeclarationBlocks() as $oDeclaration) { + $oDeclaration->createShorthands(); + } + } + + /** + * Overrides `render()` to make format argument optional. + * + * @param OutputFormat|null $oOutputFormat + * + * @return string + */ + public function render(OutputFormat $oOutputFormat = null) + { + if ($oOutputFormat === null) { + $oOutputFormat = new OutputFormat(); + } + return parent::render($oOutputFormat); + } + + /** + * @return bool + */ + public function isRootList() + { + return true; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php b/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php new file mode 100644 index 0000000..d9420e9 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php @@ -0,0 +1,104 @@ +<?php + +namespace Sabberworm\CSS\CSSList; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Property\AtRule; + +class KeyFrame extends CSSList implements AtRule +{ + /** + * @var string|null + */ + private $vendorKeyFrame; + + /** + * @var string|null + */ + private $animationName; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + parent::__construct($iLineNo); + $this->vendorKeyFrame = null; + $this->animationName = null; + } + + /** + * @param string $vendorKeyFrame + */ + public function setVendorKeyFrame($vendorKeyFrame) + { + $this->vendorKeyFrame = $vendorKeyFrame; + } + + /** + * @return string|null + */ + public function getVendorKeyFrame() + { + return $this->vendorKeyFrame; + } + + /** + * @param string $animationName + */ + public function setAnimationName($animationName) + { + $this->animationName = $animationName; + } + + /** + * @return string|null + */ + public function getAnimationName() + { + return $this->animationName; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = "@{$this->vendorKeyFrame} {$this->animationName}{$oOutputFormat->spaceBeforeOpeningBrace()}{"; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + return $sResult; + } + + /** + * @return bool + */ + public function isRootList() + { + return false; + } + + /** + * @return string|null + */ + public function atRuleName() + { + return $this->vendorKeyFrame; + } + + /** + * @return string|null + */ + public function atRuleArgs() + { + return $this->animationName; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Comment/Comment.php b/vendor/sabberworm/php-css-parser/src/Comment/Comment.php new file mode 100644 index 0000000..6128d74 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Comment/Comment.php @@ -0,0 +1,71 @@ +<?php + +namespace Sabberworm\CSS\Comment; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Renderable; + +class Comment implements Renderable +{ + /** + * @var int + */ + protected $iLineNo; + + /** + * @var string + */ + protected $sComment; + + /** + * @param string $sComment + * @param int $iLineNo + */ + public function __construct($sComment = '', $iLineNo = 0) + { + $this->sComment = $sComment; + $this->iLineNo = $iLineNo; + } + + /** + * @return string + */ + public function getComment() + { + return $this->sComment; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param string $sComment + * + * @return void + */ + public function setComment($sComment) + { + $this->sComment = $sComment; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return '/*' . $this->sComment . '*/'; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php b/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php new file mode 100644 index 0000000..5e450bf --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php @@ -0,0 +1,25 @@ +<?php + +namespace Sabberworm\CSS\Comment; + +interface Commentable +{ + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function addComments(array $aComments); + + /** + * @return array<array-key, Comment> + */ + public function getComments(); + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function setComments(array $aComments); +} diff --git a/vendor/sabberworm/php-css-parser/src/OutputFormat.php b/vendor/sabberworm/php-css-parser/src/OutputFormat.php new file mode 100644 index 0000000..595d306 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/OutputFormat.php @@ -0,0 +1,334 @@ +<?php + +namespace Sabberworm\CSS; + +/** + * Class OutputFormat + * + * @method OutputFormat setSemicolonAfterLastRule(bool $bSemicolonAfterLastRule) Set whether semicolons are added after + * last rule. + */ +class OutputFormat +{ + /** + * Value format: `"` means double-quote, `'` means single-quote + * + * @var string + */ + public $sStringQuotingType = '"'; + + /** + * Output RGB colors in hash notation if possible + * + * @var string + */ + public $bRGBHashNotation = true; + + /** + * Declaration format + * + * Semicolon after the last rule of a declaration block can be omitted. To do that, set this false. + * + * @var bool + */ + public $bSemicolonAfterLastRule = true; + + /** + * Spacing + * Note that these strings are not sanity-checked: the value should only consist of whitespace + * Any newline character will be indented according to the current level. + * The triples (After, Before, Between) can be set using a wildcard (e.g. `$oFormat->set('Space*Rules', "\n");`) + */ + public $sSpaceAfterRuleName = ' '; + + /** + * @var string + */ + public $sSpaceBeforeRules = ''; + + /** + * @var string + */ + public $sSpaceAfterRules = ''; + + /** + * @var string + */ + public $sSpaceBetweenRules = ''; + + /** + * @var string + */ + public $sSpaceBeforeBlocks = ''; + + /** + * @var string + */ + public $sSpaceAfterBlocks = ''; + + /** + * @var string + */ + public $sSpaceBetweenBlocks = "\n"; + + /** + * Content injected in and around at-rule blocks. + * + * @var string + */ + public $sBeforeAtRuleBlock = ''; + + /** + * @var string + */ + public $sAfterAtRuleBlock = ''; + + /** + * This is what’s printed before and after the comma if a declaration block contains multiple selectors. + * + * @var string + */ + public $sSpaceBeforeSelectorSeparator = ''; + + /** + * @var string + */ + public $sSpaceAfterSelectorSeparator = ' '; + + /** + * This is what’s printed after the comma of value lists + * + * @var string + */ + public $sSpaceBeforeListArgumentSeparator = ''; + + /** + * @var string + */ + public $sSpaceAfterListArgumentSeparator = ''; + + /** + * @var string + */ + public $sSpaceBeforeOpeningBrace = ' '; + + /** + * Content injected in and around declaration blocks. + * + * @var string + */ + public $sBeforeDeclarationBlock = ''; + + /** + * @var string + */ + public $sAfterDeclarationBlockSelectors = ''; + + /** + * @var string + */ + public $sAfterDeclarationBlock = ''; + + /** + * Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings. + * + * @var string + */ + public $sIndentation = "\t"; + + /** + * Output exceptions. + * + * @var bool + */ + public $bIgnoreExceptions = false; + + /** + * @var OutputFormatter|null + */ + private $oFormatter = null; + + /** + * @var OutputFormat|null + */ + private $oNextLevelFormat = null; + + /** + * @var int + */ + private $iIndentationLevel = 0; + + public function __construct() + { + } + + /** + * @param string $sName + * + * @return string|null + */ + public function get($sName) + { + $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i']; + foreach ($aVarPrefixes as $sPrefix) { + $sFieldName = $sPrefix . ucfirst($sName); + if (isset($this->$sFieldName)) { + return $this->$sFieldName; + } + } + return null; + } + + /** + * @param array<array-key, string>|string $aNames + * @param mixed $mValue + * + * @return self|false + */ + public function set($aNames, $mValue) + { + $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i']; + if (is_string($aNames) && strpos($aNames, '*') !== false) { + $aNames = + [ + str_replace('*', 'Before', $aNames), + str_replace('*', 'Between', $aNames), + str_replace('*', 'After', $aNames), + ]; + } elseif (!is_array($aNames)) { + $aNames = [$aNames]; + } + foreach ($aVarPrefixes as $sPrefix) { + $bDidReplace = false; + foreach ($aNames as $sName) { + $sFieldName = $sPrefix . ucfirst($sName); + if (isset($this->$sFieldName)) { + $this->$sFieldName = $mValue; + $bDidReplace = true; + } + } + if ($bDidReplace) { + return $this; + } + } + // Break the chain so the user knows this option is invalid + return false; + } + + /** + * @param string $sMethodName + * @param array<array-key, mixed> $aArguments + * + * @return mixed + * + * @throws \Exception + */ + public function __call($sMethodName, array $aArguments) + { + if (strpos($sMethodName, 'set') === 0) { + return $this->set(substr($sMethodName, 3), $aArguments[0]); + } elseif (strpos($sMethodName, 'get') === 0) { + return $this->get(substr($sMethodName, 3)); + } elseif (method_exists(OutputFormatter::class, $sMethodName)) { + return call_user_func_array([$this->getFormatter(), $sMethodName], $aArguments); + } else { + throw new \Exception('Unknown OutputFormat method called: ' . $sMethodName); + } + } + + /** + * @param int $iNumber + * + * @return self + */ + public function indentWithTabs($iNumber = 1) + { + return $this->setIndentation(str_repeat("\t", $iNumber)); + } + + /** + * @param int $iNumber + * + * @return self + */ + public function indentWithSpaces($iNumber = 2) + { + return $this->setIndentation(str_repeat(" ", $iNumber)); + } + + /** + * @return OutputFormat + */ + public function nextLevel() + { + if ($this->oNextLevelFormat === null) { + $this->oNextLevelFormat = clone $this; + $this->oNextLevelFormat->iIndentationLevel++; + $this->oNextLevelFormat->oFormatter = null; + } + return $this->oNextLevelFormat; + } + + /** + * @return void + */ + public function beLenient() + { + $this->bIgnoreExceptions = true; + } + + /** + * @return OutputFormatter + */ + public function getFormatter() + { + if ($this->oFormatter === null) { + $this->oFormatter = new OutputFormatter($this); + } + return $this->oFormatter; + } + + /** + * @return int + */ + public function level() + { + return $this->iIndentationLevel; + } + + /** + * Creates an instance of this class without any particular formatting settings. + * + * @return self + */ + public static function create() + { + return new OutputFormat(); + } + + /** + * Creates an instance of this class with a preset for compact formatting. + * + * @return self + */ + public static function createCompact() + { + $format = self::create(); + $format->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('') + ->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator(''); + return $format; + } + + /** + * Creates an instance of this class with a preset for pretty formatting. + * + * @return self + */ + public static function createPretty() + { + $format = self::create(); + $format->set('Space*Rules', "\n")->set('Space*Blocks', "\n") + ->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', ['default' => '', ',' => ' ']); + return $format; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/OutputFormatter.php b/vendor/sabberworm/php-css-parser/src/OutputFormatter.php new file mode 100644 index 0000000..535feca --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/OutputFormatter.php @@ -0,0 +1,231 @@ +<?php + +namespace Sabberworm\CSS; + +use Sabberworm\CSS\Parsing\OutputException; + +class OutputFormatter +{ + /** + * @var OutputFormat + */ + private $oFormat; + + public function __construct(OutputFormat $oFormat) + { + $this->oFormat = $oFormat; + } + + /** + * @param string $sName + * @param string|null $sType + * + * @return string + */ + public function space($sName, $sType = null) + { + $sSpaceString = $this->oFormat->get("Space$sName"); + // If $sSpaceString is an array, we have multiple values configured + // depending on the type of object the space applies to + if (is_array($sSpaceString)) { + if ($sType !== null && isset($sSpaceString[$sType])) { + $sSpaceString = $sSpaceString[$sType]; + } else { + $sSpaceString = reset($sSpaceString); + } + } + return $this->prepareSpace($sSpaceString); + } + + /** + * @return string + */ + public function spaceAfterRuleName() + { + return $this->space('AfterRuleName'); + } + + /** + * @return string + */ + public function spaceBeforeRules() + { + return $this->space('BeforeRules'); + } + + /** + * @return string + */ + public function spaceAfterRules() + { + return $this->space('AfterRules'); + } + + /** + * @return string + */ + public function spaceBetweenRules() + { + return $this->space('BetweenRules'); + } + + /** + * @return string + */ + public function spaceBeforeBlocks() + { + return $this->space('BeforeBlocks'); + } + + /** + * @return string + */ + public function spaceAfterBlocks() + { + return $this->space('AfterBlocks'); + } + + /** + * @return string + */ + public function spaceBetweenBlocks() + { + return $this->space('BetweenBlocks'); + } + + /** + * @return string + */ + public function spaceBeforeSelectorSeparator() + { + return $this->space('BeforeSelectorSeparator'); + } + + /** + * @return string + */ + public function spaceAfterSelectorSeparator() + { + return $this->space('AfterSelectorSeparator'); + } + + /** + * @param string $sSeparator + * + * @return string + */ + public function spaceBeforeListArgumentSeparator($sSeparator) + { + return $this->space('BeforeListArgumentSeparator', $sSeparator); + } + + /** + * @param string $sSeparator + * + * @return string + */ + public function spaceAfterListArgumentSeparator($sSeparator) + { + return $this->space('AfterListArgumentSeparator', $sSeparator); + } + + /** + * @return string + */ + public function spaceBeforeOpeningBrace() + { + return $this->space('BeforeOpeningBrace'); + } + + /** + * Runs the given code, either swallowing or passing exceptions, depending on the `bIgnoreExceptions` setting. + * + * @param string $cCode the name of the function to call + * + * @return string|null + */ + public function safely($cCode) + { + if ($this->oFormat->get('IgnoreExceptions')) { + // If output exceptions are ignored, run the code with exception guards + try { + return $cCode(); + } catch (OutputException $e) { + return null; + } // Do nothing + } else { + // Run the code as-is + return $cCode(); + } + } + + /** + * Clone of the `implode` function, but calls `render` with the current output format instead of `__toString()`. + * + * @param string $sSeparator + * @param array<array-key, Renderable|string> $aValues + * @param bool $bIncreaseLevel + * + * @return string + */ + public function implode($sSeparator, array $aValues, $bIncreaseLevel = false) + { + $sResult = ''; + $oFormat = $this->oFormat; + if ($bIncreaseLevel) { + $oFormat = $oFormat->nextLevel(); + } + $bIsFirst = true; + foreach ($aValues as $mValue) { + if ($bIsFirst) { + $bIsFirst = false; + } else { + $sResult .= $sSeparator; + } + if ($mValue instanceof Renderable) { + $sResult .= $mValue->render($oFormat); + } else { + $sResult .= $mValue; + } + } + return $sResult; + } + + /** + * @param string $sString + * + * @return string + */ + public function removeLastSemicolon($sString) + { + if ($this->oFormat->get('SemicolonAfterLastRule')) { + return $sString; + } + $sString = explode(';', $sString); + if (count($sString) < 2) { + return $sString[0]; + } + $sLast = array_pop($sString); + $sNextToLast = array_pop($sString); + array_push($sString, $sNextToLast . $sLast); + return implode(';', $sString); + } + + /** + * @param string $sSpaceString + * + * @return string + */ + private function prepareSpace($sSpaceString) + { + return str_replace("\n", "\n" . $this->indent(), $sSpaceString); + } + + /** + * @return string + */ + private function indent() + { + return str_repeat($this->oFormat->sIndentation, $this->oFormat->level()); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Parser.php b/vendor/sabberworm/php-css-parser/src/Parser.php new file mode 100644 index 0000000..f3b0493 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Parser.php @@ -0,0 +1,60 @@ +<?php + +namespace Sabberworm\CSS; + +use Sabberworm\CSS\CSSList\Document; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\SourceException; + +/** + * This class parses CSS from text into a data structure. + */ +class Parser +{ + /** + * @var ParserState + */ + private $oParserState; + + /** + * @param string $sText + * @param Settings|null $oParserSettings + * @param int $iLineNo the line number (starting from 1, not from 0) + */ + public function __construct($sText, Settings $oParserSettings = null, $iLineNo = 1) + { + if ($oParserSettings === null) { + $oParserSettings = Settings::create(); + } + $this->oParserState = new ParserState($sText, $oParserSettings, $iLineNo); + } + + /** + * @param string $sCharset + * + * @return void + */ + public function setCharset($sCharset) + { + $this->oParserState->setCharset($sCharset); + } + + /** + * @return void + */ + public function getCharset() + { + // Note: The `return` statement is missing here. This is a bug that needs to be fixed. + $this->oParserState->getCharset(); + } + + /** + * @return Document + * + * @throws SourceException + */ + public function parse() + { + return Document::parse($this->oParserState); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php b/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php new file mode 100644 index 0000000..9bfbc75 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php @@ -0,0 +1,18 @@ +<?php + +namespace Sabberworm\CSS\Parsing; + +/** + * Thrown if the CSS parser attempts to print something invalid. + */ +class OutputException extends SourceException +{ + /** + * @param string $sMessage + * @param int $iLineNo + */ + public function __construct($sMessage, $iLineNo = 0) + { + parent::__construct($sMessage, $iLineNo); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Parsing/ParserState.php b/vendor/sabberworm/php-css-parser/src/Parsing/ParserState.php new file mode 100644 index 0000000..e7d85ee --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Parsing/ParserState.php @@ -0,0 +1,516 @@ +<?php + +namespace Sabberworm\CSS\Parsing; + +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\Settings; + +class ParserState +{ + /** + * @var null + */ + const EOF = null; + + /** + * @var Settings + */ + private $oParserSettings; + + /** + * @var string + */ + private $sText; + + /** + * @var array<int, string> + */ + private $aText; + + /** + * @var int + */ + private $iCurrentPosition; + + /** + * @var string + */ + private $sCharset; + + /** + * @var int + */ + private $iLength; + + /** + * @var int + */ + private $iLineNo; + + /** + * @param string $sText + * @param int $iLineNo + */ + public function __construct($sText, Settings $oParserSettings, $iLineNo = 1) + { + $this->oParserSettings = $oParserSettings; + $this->sText = $sText; + $this->iCurrentPosition = 0; + $this->iLineNo = $iLineNo; + $this->setCharset($this->oParserSettings->sDefaultCharset); + } + + /** + * @param string $sCharset + * + * @return void + */ + public function setCharset($sCharset) + { + $this->sCharset = $sCharset; + $this->aText = $this->strsplit($this->sText); + if (is_array($this->aText)) { + $this->iLength = count($this->aText); + } + } + + /** + * @return string + */ + public function getCharset() + { + return $this->sCharset; + } + + /** + * @return int + */ + public function currentLine() + { + return $this->iLineNo; + } + + /** + * @return int + */ + public function currentColumn() + { + return $this->iCurrentPosition; + } + + /** + * @return Settings + */ + public function getSettings() + { + return $this->oParserSettings; + } + + /** + * @param bool $bIgnoreCase + * + * @return string + * + * @throws UnexpectedTokenException + */ + public function parseIdentifier($bIgnoreCase = true) + { + $sResult = $this->parseCharacter(true); + if ($sResult === null) { + throw new UnexpectedTokenException($sResult, $this->peek(5), 'identifier', $this->iLineNo); + } + $sCharacter = null; + while (($sCharacter = $this->parseCharacter(true)) !== null) { + if (preg_match('/[a-zA-Z0-9\x{00A0}-\x{FFFF}_-]/Sux', $sCharacter)) { + $sResult .= $sCharacter; + } else { + $sResult .= '\\' . $sCharacter; + } + } + if ($bIgnoreCase) { + $sResult = $this->strtolower($sResult); + } + return $sResult; + } + + /** + * @param bool $bIsForIdentifier + * + * @return string|null + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function parseCharacter($bIsForIdentifier) + { + if ($this->peek() === '\\') { + if ( + $bIsForIdentifier && $this->oParserSettings->bLenientParsing + && ($this->comes('\0') || $this->comes('\9')) + ) { + // Non-strings can contain \0 or \9 which is an IE hack supported in lenient parsing. + return null; + } + $this->consume('\\'); + if ($this->comes('\n') || $this->comes('\r')) { + return ''; + } + if (preg_match('/[0-9a-fA-F]/Su', $this->peek()) === 0) { + return $this->consume(1); + } + $sUnicode = $this->consumeExpression('/^[0-9a-fA-F]{1,6}/u', 6); + if ($this->strlen($sUnicode) < 6) { + // Consume whitespace after incomplete unicode escape + if (preg_match('/\\s/isSu', $this->peek())) { + if ($this->comes('\r\n')) { + $this->consume(2); + } else { + $this->consume(1); + } + } + } + $iUnicode = intval($sUnicode, 16); + $sUtf32 = ""; + for ($i = 0; $i < 4; ++$i) { + $sUtf32 .= chr($iUnicode & 0xff); + $iUnicode = $iUnicode >> 8; + } + return iconv('utf-32le', $this->sCharset, $sUtf32); + } + if ($bIsForIdentifier) { + $peek = ord($this->peek()); + // Ranges: a-z A-Z 0-9 - _ + if ( + ($peek >= 97 && $peek <= 122) + || ($peek >= 65 && $peek <= 90) + || ($peek >= 48 && $peek <= 57) + || ($peek === 45) + || ($peek === 95) + || ($peek > 0xa1) + ) { + return $this->consume(1); + } + } else { + return $this->consume(1); + } + return null; + } + + /** + * @return array<int, Comment>|void + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consumeWhiteSpace() + { + $comments = []; + do { + while (preg_match('/\\s/isSu', $this->peek()) === 1) { + $this->consume(1); + } + if ($this->oParserSettings->bLenientParsing) { + try { + $oComment = $this->consumeComment(); + } catch (UnexpectedEOFException $e) { + $this->iCurrentPosition = $this->iLength; + return; + } + } else { + $oComment = $this->consumeComment(); + } + if ($oComment !== false) { + $comments[] = $oComment; + } + } while ($oComment !== false); + return $comments; + } + + /** + * @param string $sString + * @param bool $bCaseInsensitive + * + * @return bool + */ + public function comes($sString, $bCaseInsensitive = false) + { + $sPeek = $this->peek(strlen($sString)); + return ($sPeek == '') + ? false + : $this->streql($sPeek, $sString, $bCaseInsensitive); + } + + /** + * @param int $iLength + * @param int $iOffset + * + * @return string + */ + public function peek($iLength = 1, $iOffset = 0) + { + $iOffset += $this->iCurrentPosition; + if ($iOffset >= $this->iLength) { + return ''; + } + return $this->substr($iOffset, $iLength); + } + + /** + * @param int $mValue + * + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consume($mValue = 1) + { + if (is_string($mValue)) { + $iLineCount = substr_count($mValue, "\n"); + $iLength = $this->strlen($mValue); + if (!$this->streql($this->substr($this->iCurrentPosition, $iLength), $mValue)) { + throw new UnexpectedTokenException($mValue, $this->peek(max($iLength, 5)), $this->iLineNo); + } + $this->iLineNo += $iLineCount; + $this->iCurrentPosition += $this->strlen($mValue); + return $mValue; + } else { + if ($this->iCurrentPosition + $mValue > $this->iLength) { + throw new UnexpectedEOFException($mValue, $this->peek(5), 'count', $this->iLineNo); + } + $sResult = $this->substr($this->iCurrentPosition, $mValue); + $iLineCount = substr_count($sResult, "\n"); + $this->iLineNo += $iLineCount; + $this->iCurrentPosition += $mValue; + return $sResult; + } + } + + /** + * @param string $mExpression + * @param int|null $iMaxLength + * + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consumeExpression($mExpression, $iMaxLength = null) + { + $aMatches = null; + $sInput = $iMaxLength !== null ? $this->peek($iMaxLength) : $this->inputLeft(); + if (preg_match($mExpression, $sInput, $aMatches, PREG_OFFSET_CAPTURE) === 1) { + return $this->consume($aMatches[0][0]); + } + throw new UnexpectedTokenException($mExpression, $this->peek(5), 'expression', $this->iLineNo); + } + + /** + * @return Comment|false + */ + public function consumeComment() + { + $mComment = false; + if ($this->comes('/*')) { + $iLineNo = $this->iLineNo; + $this->consume(1); + $mComment = ''; + while (($char = $this->consume(1)) !== '') { + $mComment .= $char; + if ($this->comes('*/')) { + $this->consume(2); + break; + } + } + } + + if ($mComment !== false) { + // We skip the * which was included in the comment. + return new Comment(substr($mComment, 1), $iLineNo); + } + + return $mComment; + } + + /** + * @return bool + */ + public function isEnd() + { + return $this->iCurrentPosition >= $this->iLength; + } + + /** + * @param array<array-key, string>|string $aEnd + * @param string $bIncludeEnd + * @param string $consumeEnd + * @param array<int, Comment> $comments + * + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consumeUntil($aEnd, $bIncludeEnd = false, $consumeEnd = false, array &$comments = []) + { + $aEnd = is_array($aEnd) ? $aEnd : [$aEnd]; + $out = ''; + $start = $this->iCurrentPosition; + + while (!$this->isEnd()) { + $char = $this->consume(1); + if (in_array($char, $aEnd)) { + if ($bIncludeEnd) { + $out .= $char; + } elseif (!$consumeEnd) { + $this->iCurrentPosition -= $this->strlen($char); + } + return $out; + } + $out .= $char; + if ($comment = $this->consumeComment()) { + $comments[] = $comment; + } + } + + if (in_array(self::EOF, $aEnd)) { + return $out; + } + + $this->iCurrentPosition = $start; + throw new UnexpectedEOFException( + 'One of ("' . implode('","', $aEnd) . '")', + $this->peek(5), + 'search', + $this->iLineNo + ); + } + + /** + * @return string + */ + private function inputLeft() + { + return $this->substr($this->iCurrentPosition, -1); + } + + /** + * @param string $sString1 + * @param string $sString2 + * @param bool $bCaseInsensitive + * + * @return bool + */ + public function streql($sString1, $sString2, $bCaseInsensitive = true) + { + if ($bCaseInsensitive) { + return $this->strtolower($sString1) === $this->strtolower($sString2); + } else { + return $sString1 === $sString2; + } + } + + /** + * @param int $iAmount + * + * @return void + */ + public function backtrack($iAmount) + { + $this->iCurrentPosition -= $iAmount; + } + + /** + * @param string $sString + * + * @return int + */ + public function strlen($sString) + { + if ($this->oParserSettings->bMultibyteSupport) { + return mb_strlen($sString, $this->sCharset); + } else { + return strlen($sString); + } + } + + /** + * @param int $iStart + * @param int $iLength + * + * @return string + */ + private function substr($iStart, $iLength) + { + if ($iLength < 0) { + $iLength = $this->iLength - $iStart + $iLength; + } + if ($iStart + $iLength > $this->iLength) { + $iLength = $this->iLength - $iStart; + } + $sResult = ''; + while ($iLength > 0) { + $sResult .= $this->aText[$iStart]; + $iStart++; + $iLength--; + } + return $sResult; + } + + /** + * @param string $sString + * + * @return string + */ + private function strtolower($sString) + { + if ($this->oParserSettings->bMultibyteSupport) { + return mb_strtolower($sString, $this->sCharset); + } else { + return strtolower($sString); + } + } + + /** + * @param string $sString + * + * @return array<int, string> + */ + private function strsplit($sString) + { + if ($this->oParserSettings->bMultibyteSupport) { + if ($this->streql($this->sCharset, 'utf-8')) { + return preg_split('//u', $sString, -1, PREG_SPLIT_NO_EMPTY); + } else { + $iLength = mb_strlen($sString, $this->sCharset); + $aResult = []; + for ($i = 0; $i < $iLength; ++$i) { + $aResult[] = mb_substr($sString, $i, 1, $this->sCharset); + } + return $aResult; + } + } else { + if ($sString === '') { + return []; + } else { + return str_split($sString); + } + } + } + + /** + * @param string $sString + * @param string $sNeedle + * @param int $iOffset + * + * @return int|false + */ + private function strpos($sString, $sNeedle, $iOffset) + { + if ($this->oParserSettings->bMultibyteSupport) { + return mb_strpos($sString, $sNeedle, $iOffset, $this->sCharset); + } else { + return strpos($sString, $sNeedle, $iOffset); + } + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php b/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php new file mode 100644 index 0000000..1ca668a --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php @@ -0,0 +1,32 @@ +<?php + +namespace Sabberworm\CSS\Parsing; + +class SourceException extends \Exception +{ + /** + * @var int + */ + private $iLineNo; + + /** + * @param string $sMessage + * @param int $iLineNo + */ + public function __construct($sMessage, $iLineNo = 0) + { + $this->iLineNo = $iLineNo; + if (!empty($iLineNo)) { + $sMessage .= " [line no: $iLineNo]"; + } + parent::__construct($sMessage); + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php b/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php new file mode 100644 index 0000000..368ec70 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php @@ -0,0 +1,12 @@ +<?php + +namespace Sabberworm\CSS\Parsing; + +/** + * Thrown if the CSS parser encounters end of file it did not expect. + * + * Extends `UnexpectedTokenException` in order to preserve backwards compatibility. + */ +class UnexpectedEOFException extends UnexpectedTokenException +{ +} diff --git a/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedTokenException.php b/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedTokenException.php new file mode 100644 index 0000000..7d5b1a6 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedTokenException.php @@ -0,0 +1,51 @@ +<?php + +namespace Sabberworm\CSS\Parsing; + +/** + * Thrown if the CSS parser encounters a token it did not expect. + */ +class UnexpectedTokenException extends SourceException +{ + /** + * @var string + */ + private $sExpected; + + /** + * @var string + */ + private $sFound; + + /** + * Possible values: literal, identifier, count, expression, search + * + * @var string + */ + private $sMatchType; + + /** + * @param string $sExpected + * @param string $sFound + * @param string $sMatchType + * @param int $iLineNo + */ + public function __construct($sExpected, $sFound, $sMatchType = 'literal', $iLineNo = 0) + { + $this->sExpected = $sExpected; + $this->sFound = $sFound; + $this->sMatchType = $sMatchType; + $sMessage = "Token “{$sExpected}” ({$sMatchType}) not found. Got “{$sFound}”."; + if ($this->sMatchType === 'search') { + $sMessage = "Search for “{$sExpected}” returned no results. Context: “{$sFound}”."; + } elseif ($this->sMatchType === 'count') { + $sMessage = "Next token was expected to have {$sExpected} chars. Context: “{$sFound}”."; + } elseif ($this->sMatchType === 'identifier') { + $sMessage = "Identifier expected. Got “{$sFound}”"; + } elseif ($this->sMatchType === 'custom') { + $sMessage = trim("$sExpected $sFound"); + } + + parent::__construct($sMessage, $iLineNo); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Property/AtRule.php b/vendor/sabberworm/php-css-parser/src/Property/AtRule.php new file mode 100644 index 0000000..9536ff5 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Property/AtRule.php @@ -0,0 +1,34 @@ +<?php + +namespace Sabberworm\CSS\Property; + +use Sabberworm\CSS\Comment\Commentable; +use Sabberworm\CSS\Renderable; + +interface AtRule extends Renderable, Commentable +{ + /** + * Since there are more set rules than block rules, + * we’re whitelisting the block rules and have anything else be treated as a set rule. + * + * @var string + */ + const BLOCK_RULES = 'media/document/supports/region-style/font-feature-values'; + + /** + * … and more font-specific ones (to be used inside font-feature-values) + * + * @var string + */ + const SET_RULES = 'font-face/counter-style/page/swash/styleset/annotation'; + + /** + * @return string|null + */ + public function atRuleName(); + + /** + * @return string|null + */ + public function atRuleArgs(); +} diff --git a/vendor/sabberworm/php-css-parser/src/Property/CSSNamespace.php b/vendor/sabberworm/php-css-parser/src/Property/CSSNamespace.php new file mode 100644 index 0000000..0d7eb49 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Property/CSSNamespace.php @@ -0,0 +1,154 @@ +<?php + +namespace Sabberworm\CSS\Property; + +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\OutputFormat; + +/** + * `CSSNamespace` represents an `@namespace` rule. + */ +class CSSNamespace implements AtRule +{ + /** + * @var string + */ + private $mUrl; + + /** + * @var string + */ + private $sPrefix; + + /** + * @var int + */ + private $iLineNo; + + /** + * @var array<array-key, Comment> + */ + protected $aComments; + + /** + * @param string $mUrl + * @param string|null $sPrefix + * @param int $iLineNo + */ + public function __construct($mUrl, $sPrefix = null, $iLineNo = 0) + { + $this->mUrl = $mUrl; + $this->sPrefix = $sPrefix; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return '@namespace ' . ($this->sPrefix === null ? '' : $this->sPrefix . ' ') + . $this->mUrl->render($oOutputFormat) . ';'; + } + + /** + * @return string + */ + public function getUrl() + { + return $this->mUrl; + } + + /** + * @return string|null + */ + public function getPrefix() + { + return $this->sPrefix; + } + + /** + * @param string $mUrl + * + * @return void + */ + public function setUrl($mUrl) + { + $this->mUrl = $mUrl; + } + + /** + * @param string $sPrefix + * + * @return void + */ + public function setPrefix($sPrefix) + { + $this->sPrefix = $sPrefix; + } + + /** + * @return string + */ + public function atRuleName() + { + return 'namespace'; + } + + /** + * @return array<int, string> + */ + public function atRuleArgs() + { + $aResult = [$this->mUrl]; + if ($this->sPrefix) { + array_unshift($aResult, $this->sPrefix); + } + return $aResult; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array<array-key, Comment> + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Property/Charset.php b/vendor/sabberworm/php-css-parser/src/Property/Charset.php new file mode 100644 index 0000000..3ee0c3d --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Property/Charset.php @@ -0,0 +1,129 @@ +<?php + +namespace Sabberworm\CSS\Property; + +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\OutputFormat; + +/** + * Class representing an `@charset` rule. + * + * The following restrictions apply: + * - May not be found in any CSSList other than the Document. + * - May only appear at the very top of a Document’s contents. + * - Must not appear more than once. + */ +class Charset implements AtRule +{ + /** + * @var string + */ + private $sCharset; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @var array<array-key, Comment> + */ + protected $aComments; + + /** + * @param string $sCharset + * @param int $iLineNo + */ + public function __construct($sCharset, $iLineNo = 0) + { + $this->sCharset = $sCharset; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param string $sCharset + * + * @return void + */ + public function setCharset($sCharset) + { + $this->sCharset = $sCharset; + } + + /** + * @return string + */ + public function getCharset() + { + return $this->sCharset; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return "@charset {$this->sCharset->render($oOutputFormat)};"; + } + + /** + * @return string + */ + public function atRuleName() + { + return 'charset'; + } + + /** + * @return string + */ + public function atRuleArgs() + { + return $this->sCharset; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array<array-key, Comment> + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Property/Import.php b/vendor/sabberworm/php-css-parser/src/Property/Import.php new file mode 100644 index 0000000..a225301 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Property/Import.php @@ -0,0 +1,137 @@ +<?php + +namespace Sabberworm\CSS\Property; + +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Value\URL; + +/** + * Class representing an `@import` rule. + */ +class Import implements AtRule +{ + /** + * @var URL + */ + private $oLocation; + + /** + * @var string + */ + private $sMediaQuery; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @var array<array-key, Comment> + */ + protected $aComments; + + /** + * @param URL $oLocation + * @param string $sMediaQuery + * @param int $iLineNo + */ + public function __construct(URL $oLocation, $sMediaQuery, $iLineNo = 0) + { + $this->oLocation = $oLocation; + $this->sMediaQuery = $sMediaQuery; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param URL $oLocation + * + * @return void + */ + public function setLocation($oLocation) + { + $this->oLocation = $oLocation; + } + + /** + * @return URL + */ + public function getLocation() + { + return $this->oLocation; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return "@import " . $this->oLocation->render($oOutputFormat) + . ($this->sMediaQuery === null ? '' : ' ' . $this->sMediaQuery) . ';'; + } + + /** + * @return string + */ + public function atRuleName() + { + return 'import'; + } + + /** + * @return array<int, URL|string> + */ + public function atRuleArgs() + { + $aResult = [$this->oLocation]; + if ($this->sMediaQuery) { + array_push($aResult, $this->sMediaQuery); + } + return $aResult; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array<array-key, Comment> + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php b/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php new file mode 100644 index 0000000..14ea5eb --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php @@ -0,0 +1,23 @@ +<?php + +namespace Sabberworm\CSS\Property; + +class KeyframeSelector extends Selector +{ + /** + * regexp for specificity calculations + * + * @var string + */ + const SELECTOR_VALIDATION_RX = '/ + ^( + (?: + [a-zA-Z0-9\x{00A0}-\x{FFFF}_^$|*="\'~\[\]()\-\s\.:#+>]* # any sequence of valid unescaped characters + (?:\\\\.)? # a single escaped character + (?:([\'"]).*?(?<!\\\\)\2)? # a quoted text like [id="example"] + )* + )| + (\d+%) # keyframe animation progress percentage (e.g. 50%) + $ + /ux'; +} diff --git a/vendor/sabberworm/php-css-parser/src/Property/Selector.php b/vendor/sabberworm/php-css-parser/src/Property/Selector.php new file mode 100644 index 0000000..70c9b2f --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Property/Selector.php @@ -0,0 +1,138 @@ +<?php + +namespace Sabberworm\CSS\Property; + +/** + * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this + * class. + */ +class Selector +{ + /** + * regexp for specificity calculations + * + * @var string + */ + const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/ + (\.[\w]+) # classes + | + \[(\w+) # attributes + | + (\:( # pseudo classes + link|visited|active + |hover|focus + |lang + |target + |enabled|disabled|checked|indeterminate + |root + |nth-child|nth-last-child|nth-of-type|nth-last-of-type + |first-child|last-child|first-of-type|last-of-type + |only-child|only-of-type + |empty|contains + )) + /ix'; + + /** + * regexp for specificity calculations + * + * @var string + */ + const ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/ + ((^|[\s\+\>\~]+)[\w]+ # elements + | + \:{1,2}( # pseudo-elements + after|before|first-letter|first-line|selection + )) + /ix'; + + /** + * regexp for specificity calculations + * + * @var string + */ + const SELECTOR_VALIDATION_RX = '/ + ^( + (?: + [a-zA-Z0-9\x{00A0}-\x{FFFF}_^$|*="\'~\[\]()\-\s\.:#+>]* # any sequence of valid unescaped characters + (?:\\\\.)? # a single escaped character + (?:([\'"]).*?(?<!\\\\)\2)? # a quoted text like [id="example"] + )* + )$ + /ux'; + + /** + * @var string + */ + private $sSelector; + + /** + * @var int|null + */ + private $iSpecificity; + + /** + * @param string $sSelector + * + * @return bool + */ + public static function isValid($sSelector) + { + return preg_match(static::SELECTOR_VALIDATION_RX, $sSelector); + } + + /** + * @param string $sSelector + * @param bool $bCalculateSpecificity + */ + public function __construct($sSelector, $bCalculateSpecificity = false) + { + $this->setSelector($sSelector); + if ($bCalculateSpecificity) { + $this->getSpecificity(); + } + } + + /** + * @return string + */ + public function getSelector() + { + return $this->sSelector; + } + + /** + * @param string $sSelector + * + * @return void + */ + public function setSelector($sSelector) + { + $this->sSelector = trim($sSelector); + $this->iSpecificity = null; + } + + /** + * @return string + */ + public function __toString() + { + return $this->getSelector(); + } + + /** + * @return int + */ + public function getSpecificity() + { + if ($this->iSpecificity === null) { + $a = 0; + /// @todo should exclude \# as well as "#" + $aMatches = null; + $b = substr_count($this->sSelector, '#'); + $c = preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $this->sSelector, $aMatches); + $d = preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $this->sSelector, $aMatches); + $this->iSpecificity = ($a * 1000) + ($b * 100) + ($c * 10) + $d; + } + return $this->iSpecificity; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Renderable.php b/vendor/sabberworm/php-css-parser/src/Renderable.php new file mode 100644 index 0000000..dc1bff3 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Renderable.php @@ -0,0 +1,21 @@ +<?php + +namespace Sabberworm\CSS; + +interface Renderable +{ + /** + * @return string + */ + public function __toString(); + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat); + + /** + * @return int + */ + public function getLineNo(); +} diff --git a/vendor/sabberworm/php-css-parser/src/Rule/Rule.php b/vendor/sabberworm/php-css-parser/src/Rule/Rule.php new file mode 100644 index 0000000..c1ea6df --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Rule/Rule.php @@ -0,0 +1,392 @@ +<?php + +namespace Sabberworm\CSS\Rule; + +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\Comment\Commentable; +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; +use Sabberworm\CSS\Renderable; +use Sabberworm\CSS\Value\RuleValueList; +use Sabberworm\CSS\Value\Value; + +/** + * RuleSets contains Rule objects which always have a key and a value. + * In CSS, Rules are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];” + */ +class Rule implements Renderable, Commentable +{ + /** + * @var string + */ + private $sRule; + + /** + * @var RuleValueList|null + */ + private $mValue; + + /** + * @var bool + */ + private $bIsImportant; + + /** + * @var array<int, int> + */ + private $aIeHack; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @var int + */ + protected $iColNo; + + /** + * @var array<array-key, Comment> + */ + protected $aComments; + + /** + * @param string $sRule + * @param int $iLineNo + * @param int $iColNo + */ + public function __construct($sRule, $iLineNo = 0, $iColNo = 0) + { + $this->sRule = $sRule; + $this->mValue = null; + $this->bIsImportant = false; + $this->aIeHack = []; + $this->iLineNo = $iLineNo; + $this->iColNo = $iColNo; + $this->aComments = []; + } + + /** + * @return Rule + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $aComments = $oParserState->consumeWhiteSpace(); + $oRule = new Rule( + $oParserState->parseIdentifier(!$oParserState->comes("--")), + $oParserState->currentLine(), + $oParserState->currentColumn() + ); + $oRule->setComments($aComments); + $oRule->addComments($oParserState->consumeWhiteSpace()); + $oParserState->consume(':'); + $oValue = Value::parseValue($oParserState, self::listDelimiterForRule($oRule->getRule())); + $oRule->setValue($oValue); + if ($oParserState->getSettings()->bLenientParsing) { + while ($oParserState->comes('\\')) { + $oParserState->consume('\\'); + $oRule->addIeHack($oParserState->consume()); + $oParserState->consumeWhiteSpace(); + } + } + $oParserState->consumeWhiteSpace(); + if ($oParserState->comes('!')) { + $oParserState->consume('!'); + $oParserState->consumeWhiteSpace(); + $oParserState->consume('important'); + $oRule->setIsImportant(true); + } + $oParserState->consumeWhiteSpace(); + while ($oParserState->comes(';')) { + $oParserState->consume(';'); + } + $oParserState->consumeWhiteSpace(); + + return $oRule; + } + + /** + * @param string $sRule + * + * @return array<int, string> + */ + private static function listDelimiterForRule($sRule) + { + if (preg_match('/^font($|-)/', $sRule)) { + return [',', '/', ' ']; + } + return [',', ' ', '/']; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @return int + */ + public function getColNo() + { + return $this->iColNo; + } + + /** + * @param int $iLine + * @param int $iColumn + * + * @return void + */ + public function setPosition($iLine, $iColumn) + { + $this->iColNo = $iColumn; + $this->iLineNo = $iLine; + } + + /** + * @param string $sRule + * + * @return void + */ + public function setRule($sRule) + { + $this->sRule = $sRule; + } + + /** + * @return string + */ + public function getRule() + { + return $this->sRule; + } + + /** + * @return RuleValueList|null + */ + public function getValue() + { + return $this->mValue; + } + + /** + * @param RuleValueList|null $mValue + * + * @return void + */ + public function setValue($mValue) + { + $this->mValue = $mValue; + } + + /** + * @param array<array-key, array<array-key, RuleValueList>> $aSpaceSeparatedValues + * + * @return RuleValueList + * + * @deprecated will be removed in version 9.0 + * Old-Style 2-dimensional array given. Retained for (some) backwards-compatibility. + * Use `setValue()` instead and wrap the value inside a RuleValueList if necessary. + */ + public function setValues(array $aSpaceSeparatedValues) + { + $oSpaceSeparatedList = null; + if (count($aSpaceSeparatedValues) > 1) { + $oSpaceSeparatedList = new RuleValueList(' ', $this->iLineNo); + } + foreach ($aSpaceSeparatedValues as $aCommaSeparatedValues) { + $oCommaSeparatedList = null; + if (count($aCommaSeparatedValues) > 1) { + $oCommaSeparatedList = new RuleValueList(',', $this->iLineNo); + } + foreach ($aCommaSeparatedValues as $mValue) { + if (!$oSpaceSeparatedList && !$oCommaSeparatedList) { + $this->mValue = $mValue; + return $mValue; + } + if ($oCommaSeparatedList) { + $oCommaSeparatedList->addListComponent($mValue); + } else { + $oSpaceSeparatedList->addListComponent($mValue); + } + } + if (!$oSpaceSeparatedList) { + $this->mValue = $oCommaSeparatedList; + return $oCommaSeparatedList; + } else { + $oSpaceSeparatedList->addListComponent($oCommaSeparatedList); + } + } + $this->mValue = $oSpaceSeparatedList; + return $oSpaceSeparatedList; + } + + /** + * @return array<int, array<int, RuleValueList>> + * + * @deprecated will be removed in version 9.0 + * Old-Style 2-dimensional array returned. Retained for (some) backwards-compatibility. + * Use `getValue()` instead and check for the existence of a (nested set of) ValueList object(s). + */ + public function getValues() + { + if (!$this->mValue instanceof RuleValueList) { + return [[$this->mValue]]; + } + if ($this->mValue->getListSeparator() === ',') { + return [$this->mValue->getListComponents()]; + } + $aResult = []; + foreach ($this->mValue->getListComponents() as $mValue) { + if (!$mValue instanceof RuleValueList || $mValue->getListSeparator() !== ',') { + $aResult[] = [$mValue]; + continue; + } + if ($this->mValue->getListSeparator() === ' ' || count($aResult) === 0) { + $aResult[] = []; + } + foreach ($mValue->getListComponents() as $mValue) { + $aResult[count($aResult) - 1][] = $mValue; + } + } + return $aResult; + } + + /** + * Adds a value to the existing value. Value will be appended if a `RuleValueList` exists of the given type. + * Otherwise, the existing value will be wrapped by one. + * + * @param RuleValueList|array<int, RuleValueList> $mValue + * @param string $sType + * + * @return void + */ + public function addValue($mValue, $sType = ' ') + { + if (!is_array($mValue)) { + $mValue = [$mValue]; + } + if (!$this->mValue instanceof RuleValueList || $this->mValue->getListSeparator() !== $sType) { + $mCurrentValue = $this->mValue; + $this->mValue = new RuleValueList($sType, $this->iLineNo); + if ($mCurrentValue) { + $this->mValue->addListComponent($mCurrentValue); + } + } + foreach ($mValue as $mValueItem) { + $this->mValue->addListComponent($mValueItem); + } + } + + /** + * @param int $iModifier + * + * @return void + */ + public function addIeHack($iModifier) + { + $this->aIeHack[] = $iModifier; + } + + /** + * @param array<int, int> $aModifiers + * + * @return void + */ + public function setIeHack(array $aModifiers) + { + $this->aIeHack = $aModifiers; + } + + /** + * @return array<int, int> + */ + public function getIeHack() + { + return $this->aIeHack; + } + + /** + * @param bool $bIsImportant + * + * @return void + */ + public function setIsImportant($bIsImportant) + { + $this->bIsImportant = $bIsImportant; + } + + /** + * @return bool + */ + public function getIsImportant() + { + return $this->bIsImportant; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}"; + if ($this->mValue instanceof Value) { //Can also be a ValueList + $sResult .= $this->mValue->render($oOutputFormat); + } else { + $sResult .= $this->mValue; + } + if (!empty($this->aIeHack)) { + $sResult .= ' \\' . implode('\\', $this->aIeHack); + } + if ($this->bIsImportant) { + $sResult .= ' !important'; + } + $sResult .= ';'; + return $sResult; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array<array-key, Comment> + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array<array-key, Comment> $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php b/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php new file mode 100644 index 0000000..88bc5bd --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php @@ -0,0 +1,73 @@ +<?php + +namespace Sabberworm\CSS\RuleSet; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Property\AtRule; + +/** + * A RuleSet constructed by an unknown at-rule. `@font-face` rules are rendered into AtRuleSet objects. + */ +class AtRuleSet extends RuleSet implements AtRule +{ + /** + * @var string + */ + private $sType; + + /** + * @var string + */ + private $sArgs; + + /** + * @param string $sType + * @param string $sArgs + * @param int $iLineNo + */ + public function __construct($sType, $sArgs = '', $iLineNo = 0) + { + parent::__construct($iLineNo); + $this->sType = $sType; + $this->sArgs = $sArgs; + } + + /** + * @return string + */ + public function atRuleName() + { + return $this->sType; + } + + /** + * @return string + */ + public function atRuleArgs() + { + return $this->sArgs; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sArgs = $this->sArgs; + if ($sArgs) { + $sArgs = ' ' . $sArgs; + } + $sResult = "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{"; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + return $sResult; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php b/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php new file mode 100644 index 0000000..c27cdd4 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php @@ -0,0 +1,831 @@ +<?php + +namespace Sabberworm\CSS\RuleSet; + +use Sabberworm\CSS\CSSList\CSSList; +use Sabberworm\CSS\CSSList\KeyFrame; +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\OutputException; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; +use Sabberworm\CSS\Property\KeyframeSelector; +use Sabberworm\CSS\Property\Selector; +use Sabberworm\CSS\Rule\Rule; +use Sabberworm\CSS\Value\Color; +use Sabberworm\CSS\Value\RuleValueList; +use Sabberworm\CSS\Value\Size; +use Sabberworm\CSS\Value\URL; +use Sabberworm\CSS\Value\Value; + +/** + * Declaration blocks are the parts of a CSS file which denote the rules belonging to a selector. + * + * Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`). + */ +class DeclarationBlock extends RuleSet +{ + /** + * @var array<int, Selector|string> + */ + private $aSelectors; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + parent::__construct($iLineNo); + $this->aSelectors = []; + } + + /** + * @param CSSList|null $oList + * + * @return DeclarationBlock|false + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parse(ParserState $oParserState, $oList = null) + { + $aComments = []; + $oResult = new DeclarationBlock($oParserState->currentLine()); + try { + $aSelectorParts = []; + $sStringWrapperChar = false; + do { + $aSelectorParts[] = $oParserState->consume(1) + . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments); + if (in_array($oParserState->peek(), ['\'', '"']) && substr(end($aSelectorParts), -1) != "\\") { + if ($sStringWrapperChar === false) { + $sStringWrapperChar = $oParserState->peek(); + } elseif ($sStringWrapperChar == $oParserState->peek()) { + $sStringWrapperChar = false; + } + } + } while (!in_array($oParserState->peek(), ['{', '}']) || $sStringWrapperChar !== false); + $oResult->setSelectors(implode('', $aSelectorParts), $oList); + if ($oParserState->comes('{')) { + $oParserState->consume(1); + } + } catch (UnexpectedTokenException $e) { + if ($oParserState->getSettings()->bLenientParsing) { + if (!$oParserState->comes('}')) { + $oParserState->consumeUntil('}', false, true); + } + return false; + } else { + throw $e; + } + } + $oResult->setComments($aComments); + RuleSet::parseRuleSet($oParserState, $oResult); + return $oResult; + } + + /** + * @param array<int, Selector|string>|string $mSelector + * @param CSSList|null $oList + * + * @throws UnexpectedTokenException + */ + public function setSelectors($mSelector, $oList = null) + { + if (is_array($mSelector)) { + $this->aSelectors = $mSelector; + } else { + $this->aSelectors = explode(',', $mSelector); + } + foreach ($this->aSelectors as $iKey => $mSelector) { + if (!($mSelector instanceof Selector)) { + if ($oList === null || !($oList instanceof KeyFrame)) { + if (!Selector::isValid($mSelector)) { + throw new UnexpectedTokenException( + "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.", + $mSelector, + "custom" + ); + } + $this->aSelectors[$iKey] = new Selector($mSelector); + } else { + if (!KeyframeSelector::isValid($mSelector)) { + throw new UnexpectedTokenException( + "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.", + $mSelector, + "custom" + ); + } + $this->aSelectors[$iKey] = new KeyframeSelector($mSelector); + } + } + } + } + + /** + * Remove one of the selectors of the block. + * + * @param Selector|string $mSelector + * + * @return bool + */ + public function removeSelector($mSelector) + { + if ($mSelector instanceof Selector) { + $mSelector = $mSelector->getSelector(); + } + foreach ($this->aSelectors as $iKey => $oSelector) { + if ($oSelector->getSelector() === $mSelector) { + unset($this->aSelectors[$iKey]); + return true; + } + } + return false; + } + + /** + * @return array<int, Selector|string> + * + * @deprecated will be removed in version 9.0; use `getSelectors()` instead + */ + public function getSelector() + { + return $this->getSelectors(); + } + + /** + * @param Selector|string $mSelector + * @param CSSList|null $oList + * + * @return void + * + * @deprecated will be removed in version 9.0; use `setSelectors()` instead + */ + public function setSelector($mSelector, $oList = null) + { + $this->setSelectors($mSelector, $oList); + } + + /** + * @return array<int, Selector|string> + */ + public function getSelectors() + { + return $this->aSelectors; + } + + /** + * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts. + * + * @return void + */ + public function expandShorthands() + { + // border must be expanded before dimensions + $this->expandBorderShorthand(); + $this->expandDimensionsShorthand(); + $this->expandFontShorthand(); + $this->expandBackgroundShorthand(); + $this->expandListStyleShorthand(); + } + + /** + * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible. + * + * @return void + */ + public function createShorthands() + { + $this->createBackgroundShorthand(); + $this->createDimensionsShorthand(); + // border must be shortened after dimensions + $this->createBorderShorthand(); + $this->createFontShorthand(); + $this->createListStyleShorthand(); + } + + /** + * Splits shorthand border declarations (e.g. `border: 1px red;`). + * + * Additional splitting happens in expandDimensionsShorthand. + * + * Multiple borders are not yet supported as of 3. + * + * @return void + */ + public function expandBorderShorthand() + { + $aBorderRules = [ + 'border', + 'border-left', + 'border-right', + 'border-top', + 'border-bottom', + ]; + $aBorderSizes = [ + 'thin', + 'medium', + 'thick', + ]; + $aRules = $this->getRulesAssoc(); + foreach ($aBorderRules as $sBorderRule) { + if (!isset($aRules[$sBorderRule])) { + continue; + } + $oRule = $aRules[$sBorderRule]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + foreach ($aValues as $mValue) { + if ($mValue instanceof Value) { + $mNewValue = clone $mValue; + } else { + $mNewValue = $mValue; + } + if ($mValue instanceof Size) { + $sNewRuleName = $sBorderRule . "-width"; + } elseif ($mValue instanceof Color) { + $sNewRuleName = $sBorderRule . "-color"; + } else { + if (in_array($mValue, $aBorderSizes)) { + $sNewRuleName = $sBorderRule . "-width"; + } else { + $sNewRuleName = $sBorderRule . "-style"; + } + } + $oNewRule = new Rule($sNewRuleName, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue([$mNewValue]); + $this->addRule($oNewRule); + } + $this->removeRule($sBorderRule); + } + } + + /** + * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`) + * into their constituent parts. + * + * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`. + * + * @return void + */ + public function expandDimensionsShorthand() + { + $aExpansions = [ + 'margin' => 'margin-%s', + 'padding' => 'padding-%s', + 'border-color' => 'border-%s-color', + 'border-style' => 'border-%s-style', + 'border-width' => 'border-%s-width', + ]; + $aRules = $this->getRulesAssoc(); + foreach ($aExpansions as $sProperty => $sExpanded) { + if (!isset($aRules[$sProperty])) { + continue; + } + $oRule = $aRules[$sProperty]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + $top = $right = $bottom = $left = null; + switch (count($aValues)) { + case 1: + $top = $right = $bottom = $left = $aValues[0]; + break; + case 2: + $top = $bottom = $aValues[0]; + $left = $right = $aValues[1]; + break; + case 3: + $top = $aValues[0]; + $left = $right = $aValues[1]; + $bottom = $aValues[2]; + break; + case 4: + $top = $aValues[0]; + $right = $aValues[1]; + $bottom = $aValues[2]; + $left = $aValues[3]; + break; + } + foreach (['top', 'right', 'bottom', 'left'] as $sPosition) { + $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue(${$sPosition}); + $this->addRule($oNewRule); + } + $this->removeRule($sProperty); + } + } + + /** + * Converts shorthand font declarations + * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`) + * into their constituent parts. + * + * @return void + */ + public function expandFontShorthand() + { + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['font'])) { + return; + } + $oRule = $aRules['font']; + // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand + $aFontProperties = [ + 'font-style' => 'normal', + 'font-variant' => 'normal', + 'font-weight' => 'normal', + 'font-size' => 'normal', + 'line-height' => 'normal', + ]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + foreach ($aValues as $mValue) { + if (!$mValue instanceof Value) { + $mValue = mb_strtolower($mValue); + } + if (in_array($mValue, ['normal', 'inherit'])) { + foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) { + if (!isset($aFontProperties[$sProperty])) { + $aFontProperties[$sProperty] = $mValue; + } + } + } elseif (in_array($mValue, ['italic', 'oblique'])) { + $aFontProperties['font-style'] = $mValue; + } elseif ($mValue == 'small-caps') { + $aFontProperties['font-variant'] = $mValue; + } elseif ( + in_array($mValue, ['bold', 'bolder', 'lighter']) + || ($mValue instanceof Size + && in_array($mValue->getSize(), range(100, 900, 100))) + ) { + $aFontProperties['font-weight'] = $mValue; + } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') { + list($oSize, $oHeight) = $mValue->getListComponents(); + $aFontProperties['font-size'] = $oSize; + $aFontProperties['line-height'] = $oHeight; + } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) { + $aFontProperties['font-size'] = $mValue; + } else { + $aFontProperties['font-family'] = $mValue; + } + } + foreach ($aFontProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->addValue($mValue); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $this->addRule($oNewRule); + } + $this->removeRule('font'); + } + + /** + * Converts shorthand background declarations + * (e.g. `background: url("chess.png") gray 50% repeat fixed;`) + * into their constituent parts. + * + * @see http://www.w3.org/TR/21/colors.html#propdef-background + * + * @return void + */ + public function expandBackgroundShorthand() + { + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['background'])) { + return; + } + $oRule = $aRules['background']; + $aBgProperties = [ + 'background-color' => ['transparent'], + 'background-image' => ['none'], + 'background-repeat' => ['repeat'], + 'background-attachment' => ['scroll'], + 'background-position' => [ + new Size(0, '%', null, false, $this->iLineNo), + new Size(0, '%', null, false, $this->iLineNo), + ], + ]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + if (count($aValues) == 1 && $aValues[0] == 'inherit') { + foreach ($aBgProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->addValue('inherit'); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $this->addRule($oNewRule); + } + $this->removeRule('background'); + return; + } + $iNumBgPos = 0; + foreach ($aValues as $mValue) { + if (!$mValue instanceof Value) { + $mValue = mb_strtolower($mValue); + } + if ($mValue instanceof URL) { + $aBgProperties['background-image'] = $mValue; + } elseif ($mValue instanceof Color) { + $aBgProperties['background-color'] = $mValue; + } elseif (in_array($mValue, ['scroll', 'fixed'])) { + $aBgProperties['background-attachment'] = $mValue; + } elseif (in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'])) { + $aBgProperties['background-repeat'] = $mValue; + } elseif ( + in_array($mValue, ['left', 'center', 'right', 'top', 'bottom']) + || $mValue instanceof Size + ) { + if ($iNumBgPos == 0) { + $aBgProperties['background-position'][0] = $mValue; + $aBgProperties['background-position'][1] = 'center'; + } else { + $aBgProperties['background-position'][$iNumBgPos] = $mValue; + } + $iNumBgPos++; + } + } + foreach ($aBgProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue($mValue); + $this->addRule($oNewRule); + } + $this->removeRule('background'); + } + + /** + * @return void + */ + public function expandListStyleShorthand() + { + $aListProperties = [ + 'list-style-type' => 'disc', + 'list-style-position' => 'outside', + 'list-style-image' => 'none', + ]; + $aListStyleTypes = [ + 'none', + 'disc', + 'circle', + 'square', + 'decimal-leading-zero', + 'decimal', + 'lower-roman', + 'upper-roman', + 'lower-greek', + 'lower-alpha', + 'lower-latin', + 'upper-alpha', + 'upper-latin', + 'hebrew', + 'armenian', + 'georgian', + 'cjk-ideographic', + 'hiragana', + 'hira-gana-iroha', + 'katakana-iroha', + 'katakana', + ]; + $aListStylePositions = [ + 'inside', + 'outside', + ]; + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['list-style'])) { + return; + } + $oRule = $aRules['list-style']; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + if (count($aValues) == 1 && $aValues[0] == 'inherit') { + foreach ($aListProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->addValue('inherit'); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $this->addRule($oNewRule); + } + $this->removeRule('list-style'); + return; + } + foreach ($aValues as $mValue) { + if (!$mValue instanceof Value) { + $mValue = mb_strtolower($mValue); + } + if ($mValue instanceof Url) { + $aListProperties['list-style-image'] = $mValue; + } elseif (in_array($mValue, $aListStyleTypes)) { + $aListProperties['list-style-types'] = $mValue; + } elseif (in_array($mValue, $aListStylePositions)) { + $aListProperties['list-style-position'] = $mValue; + } + } + foreach ($aListProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue($mValue); + $this->addRule($oNewRule); + } + $this->removeRule('list-style'); + } + + /** + * @param array<array-key, string> $aProperties + * @param string $sShorthand + * + * @return void + */ + public function createShorthandProperties(array $aProperties, $sShorthand) + { + $aRules = $this->getRulesAssoc(); + $aNewValues = []; + foreach ($aProperties as $sProperty) { + if (!isset($aRules[$sProperty])) { + continue; + } + $oRule = $aRules[$sProperty]; + if (!$oRule->getIsImportant()) { + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + foreach ($aValues as $mValue) { + $aNewValues[] = $mValue; + } + $this->removeRule($sProperty); + } + } + if (count($aNewValues)) { + $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo()); + foreach ($aNewValues as $mValue) { + $oNewRule->addValue($mValue); + } + $this->addRule($oNewRule); + } + } + + /** + * @return void + */ + public function createBackgroundShorthand() + { + $aProperties = [ + 'background-color', + 'background-image', + 'background-repeat', + 'background-position', + 'background-attachment', + ]; + $this->createShorthandProperties($aProperties, 'background'); + } + + /** + * @return void + */ + public function createListStyleShorthand() + { + $aProperties = [ + 'list-style-type', + 'list-style-position', + 'list-style-image', + ]; + $this->createShorthandProperties($aProperties, 'list-style'); + } + + /** + * Combines `border-color`, `border-style` and `border-width` into `border`. + * + * Should be run after `create_dimensions_shorthand`! + * + * @return void + */ + public function createBorderShorthand() + { + $aProperties = [ + 'border-width', + 'border-style', + 'border-color', + ]; + $this->createShorthandProperties($aProperties, 'border'); + } + + /** + * Looks for long format CSS dimensional properties + * (margin, padding, border-color, border-style and border-width) + * and converts them into shorthand CSS properties. + * + * @return void + */ + public function createDimensionsShorthand() + { + $aPositions = ['top', 'right', 'bottom', 'left']; + $aExpansions = [ + 'margin' => 'margin-%s', + 'padding' => 'padding-%s', + 'border-color' => 'border-%s-color', + 'border-style' => 'border-%s-style', + 'border-width' => 'border-%s-width', + ]; + $aRules = $this->getRulesAssoc(); + foreach ($aExpansions as $sProperty => $sExpanded) { + $aFoldable = []; + foreach ($aRules as $sRuleName => $oRule) { + foreach ($aPositions as $sPosition) { + if ($sRuleName == sprintf($sExpanded, $sPosition)) { + $aFoldable[$sRuleName] = $oRule; + } + } + } + // All four dimensions must be present + if (count($aFoldable) == 4) { + $aValues = []; + foreach ($aPositions as $sPosition) { + $oRule = $aRules[sprintf($sExpanded, $sPosition)]; + $mRuleValue = $oRule->getValue(); + $aRuleValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aRuleValues[] = $mRuleValue; + } else { + $aRuleValues = $mRuleValue->getListComponents(); + } + $aValues[$sPosition] = $aRuleValues; + } + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + if ((string)$aValues['left'][0] == (string)$aValues['right'][0]) { + if ((string)$aValues['top'][0] == (string)$aValues['bottom'][0]) { + if ((string)$aValues['top'][0] == (string)$aValues['left'][0]) { + // All 4 sides are equal + $oNewRule->addValue($aValues['top']); + } else { + // Top and bottom are equal, left and right are equal + $oNewRule->addValue($aValues['top']); + $oNewRule->addValue($aValues['left']); + } + } else { + // Only left and right are equal + $oNewRule->addValue($aValues['top']); + $oNewRule->addValue($aValues['left']); + $oNewRule->addValue($aValues['bottom']); + } + } else { + // No sides are equal + $oNewRule->addValue($aValues['top']); + $oNewRule->addValue($aValues['left']); + $oNewRule->addValue($aValues['bottom']); + $oNewRule->addValue($aValues['right']); + } + $this->addRule($oNewRule); + foreach ($aPositions as $sPosition) { + $this->removeRule(sprintf($sExpanded, $sPosition)); + } + } + } + } + + /** + * Looks for long format CSS font properties (e.g. `font-weight`) and + * tries to convert them into a shorthand CSS `font` property. + * + * At least `font-size` AND `font-family` must be present in order to create a shorthand declaration. + * + * @return void + */ + public function createFontShorthand() + { + $aFontProperties = [ + 'font-style', + 'font-variant', + 'font-weight', + 'font-size', + 'line-height', + 'font-family', + ]; + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) { + return; + } + $oOldRule = isset($aRules['font-size']) ? $aRules['font-size'] : $aRules['font-family']; + $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo()); + unset($oOldRule); + foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) { + if (isset($aRules[$sProperty])) { + $oRule = $aRules[$sProperty]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + if ($aValues[0] !== 'normal') { + $oNewRule->addValue($aValues[0]); + } + } + } + // Get the font-size value + $oRule = $aRules['font-size']; + $mRuleValue = $oRule->getValue(); + $aFSValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aFSValues[] = $mRuleValue; + } else { + $aFSValues = $mRuleValue->getListComponents(); + } + // But wait to know if we have line-height to add it + if (isset($aRules['line-height'])) { + $oRule = $aRules['line-height']; + $mRuleValue = $oRule->getValue(); + $aLHValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aLHValues[] = $mRuleValue; + } else { + $aLHValues = $mRuleValue->getListComponents(); + } + if ($aLHValues[0] !== 'normal') { + $val = new RuleValueList('/', $this->iLineNo); + $val->addListComponent($aFSValues[0]); + $val->addListComponent($aLHValues[0]); + $oNewRule->addValue($val); + } + } else { + $oNewRule->addValue($aFSValues[0]); + } + $oRule = $aRules['font-family']; + $mRuleValue = $oRule->getValue(); + $aFFValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aFFValues[] = $mRuleValue; + } else { + $aFFValues = $mRuleValue->getListComponents(); + } + $oFFValue = new RuleValueList(',', $this->iLineNo); + $oFFValue->setListComponents($aFFValues); + $oNewRule->addValue($oFFValue); + + $this->addRule($oNewRule); + foreach ($aFontProperties as $sProperty) { + $this->removeRule($sProperty); + } + } + + /** + * @return string + * + * @throws OutputException + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + * + * @throws OutputException + */ + public function render(OutputFormat $oOutputFormat) + { + if (count($this->aSelectors) === 0) { + // If all the selectors have been removed, this declaration block becomes invalid + throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo); + } + $sResult = $oOutputFormat->sBeforeDeclarationBlock; + $sResult .= $oOutputFormat->implode( + $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), + $this->aSelectors + ); + $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors; + $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{'; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + $sResult .= $oOutputFormat->sAfterDeclarationBlock; + return $sResult; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php b/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php new file mode 100644 index 0000000..9404bb0 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php @@ -0,0 +1,326 @@ +<?php + +namespace Sabberworm\CSS\RuleSet; + +use Sabberworm\CSS\Comment\Comment; +use Sabberworm\CSS\Comment\Commentable; +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; +use Sabberworm\CSS\Renderable; +use Sabberworm\CSS\Rule\Rule; + +/** + * RuleSet is a generic superclass denoting rules. The typical example for rule sets are declaration block. + * However, unknown At-Rules (like `@font-face`) are also rule sets. + */ +abstract class RuleSet implements Renderable, Commentable +{ + /** + * @var array<string, Rule> + */ + private $aRules; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @var array<array-key, Comment> + */ + protected $aComments; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + $this->aRules = []; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return void + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parseRuleSet(ParserState $oParserState, RuleSet $oRuleSet) + { + while ($oParserState->comes(';')) { + $oParserState->consume(';'); + } + while (!$oParserState->comes('}')) { + $oRule = null; + if ($oParserState->getSettings()->bLenientParsing) { + try { + $oRule = Rule::parse($oParserState); + } catch (UnexpectedTokenException $e) { + try { + $sConsume = $oParserState->consumeUntil(["\n", ";", '}'], true); + // We need to “unfind” the matches to the end of the ruleSet as this will be matched later + if ($oParserState->streql(substr($sConsume, -1), '}')) { + $oParserState->backtrack(1); + } else { + while ($oParserState->comes(';')) { + $oParserState->consume(';'); + } + } + } catch (UnexpectedTokenException $e) { + // We’ve reached the end of the document. Just close the RuleSet. + return; + } + } + } else { + $oRule = Rule::parse($oParserState); + } + if ($oRule) { + $oRuleSet->addRule($oRule); + } + } + $oParserState->consume('}'); + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param Rule|null $oSibling + * + * @return void + */ + public function addRule(Rule $oRule, Rule $oSibling = null) + { + $sRule = $oRule->getRule(); + if (!isset($this->aRules[$sRule])) { + $this->aRules[$sRule] = []; + } + + $iPosition = count($this->aRules[$sRule]); + + if ($oSibling !== null) { + $iSiblingPos = array_search($oSibling, $this->aRules[$sRule], true); + if ($iSiblingPos !== false) { + $iPosition = $iSiblingPos; + $oRule->setPosition($oSibling->getLineNo(), $oSibling->getColNo() - 1); + } + } + if ($oRule->getLineNo() === 0 && $oRule->getColNo() === 0) { + //this node is added manually, give it the next best line + $rules = $this->getRules(); + $pos = count($rules); + if ($pos > 0) { + $last = $rules[$pos - 1]; + $oRule->setPosition($last->getLineNo() + 1, 0); + } + } + + array_splice($this->aRules[$sRule], $iPosition, 0, [$oRule]); + } + + /** + * Returns all rules matching the given rule name + * + * @example $oRuleSet->getRules('font') // returns array(0 => $oRule, …) or array(). + * + * @example $oRuleSet->getRules('font-') + * //returns an array of all rules either beginning with font- or matching font. + * + * @param Rule|string|null $mRule + * Pattern to search for. If null, returns all rules. + * If the pattern ends with a dash, all rules starting with the pattern are returned + * as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves like calling `getRules($mRule->getRule())`. + * + * @return array<int, Rule> + */ + public function getRules($mRule = null) + { + if ($mRule instanceof Rule) { + $mRule = $mRule->getRule(); + } + /** @var array<int, Rule> $aResult */ + $aResult = []; + foreach ($this->aRules as $sName => $aRules) { + // Either no search rule is given or the search rule matches the found rule exactly + // or the search rule ends in “-” and the found rule starts with the search rule. + if ( + !$mRule || $sName === $mRule + || ( + strrpos($mRule, '-') === strlen($mRule) - strlen('-') + && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1)) + ) + ) { + $aResult = array_merge($aResult, $aRules); + } + } + usort($aResult, function (Rule $first, Rule $second) { + if ($first->getLineNo() === $second->getLineNo()) { + return $first->getColNo() - $second->getColNo(); + } + return $first->getLineNo() - $second->getLineNo(); + }); + return $aResult; + } + + /** + * Overrides all the rules of this set. + * + * @param array<array-key, Rule> $aRules The rules to override with. + * + * @return void + */ + public function setRules(array $aRules) + { + $this->aRules = []; + foreach ($aRules as $rule) { + $this->addRule($rule); + } + } + + /** + * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name + * as keys. This method exists mainly for backwards-compatibility and is really only partially useful. + * + * Note: This method loses some information: Calling this (with an argument of `background-`) on a declaration block + * like `{ background-color: green; background-color; rgba(0, 127, 0, 0.7); }` will only yield an associative array + * containing the rgba-valued rule while `getRules()` would yield an indexed array containing both. + * + * @param Rule|string|null $mRule $mRule + * Pattern to search for. If null, returns all rules. If the pattern ends with a dash, + * all rules starting with the pattern are returned as well as one matching the pattern with the dash + * excluded. Passing a Rule behaves like calling `getRules($mRule->getRule())`. + * + * @return array<string, Rule> + */ + public function getRulesAssoc($mRule = null) + { + /** @var array<string, Rule> $aResult */ + $aResult = []; + foreach ($this->getRules($mRule) as $oRule) { + $aResult[$oRule->getRule()] = $oRule; + } + return $aResult; + } + + /** + * Removes a rule from this RuleSet. This accepts all the possible values that `getRules()` accepts. + * + * If given a Rule, it will only remove this particular rule (by identity). + * If given a name, it will remove all rules by that name. + * + * Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would + * remove all rules with the same name. To get the old behaviour, use `removeRule($oRule->getRule())`. + * + * @param Rule|string|null $mRule + * pattern to remove. If $mRule is null, all rules are removed. If the pattern ends in a dash, + * all rules starting with the pattern are removed as well as one matching the pattern with the dash + * excluded. Passing a Rule behaves matches by identity. + * + * @return void + */ + public function removeRule($mRule) + { + if ($mRule instanceof Rule) { + $sRule = $mRule->getRule(); + if (!isset($this->aRules[$sRule])) { + return; + } + foreach ($this->aRules[$sRule] as $iKey => $oRule) { + if ($oRule === $mRule) { + unset($this->aRules[$sRule][$iKey]); + } + } + } else { + foreach ($this->aRules as $sName => $aRules) { + // Either no search rule is given or the search rule matches the found rule exactly + // or the search rule ends in “-” and the found rule starts with the search rule or equals it + // (without the trailing dash). + if ( + !$mRule || $sName === $mRule + || (strrpos($mRule, '-') === strlen($mRule) - strlen('-') + && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1))) + ) { + unset($this->aRules[$sName]); + } + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = ''; + $bIsFirst = true; + foreach ($this->aRules as $aRules) { + foreach ($aRules as $oRule) { + $sRendered = $oOutputFormat->safely(function () use ($oRule, $oOutputFormat) { + return $oRule->render($oOutputFormat->nextLevel()); + }); + if ($sRendered === null) { + continue; + } + if ($bIsFirst) { + $bIsFirst = false; + $sResult .= $oOutputFormat->nextLevel()->spaceBeforeRules(); + } else { + $sResult .= $oOutputFormat->nextLevel()->spaceBetweenRules(); + } + $sResult .= $sRendered; + } + } + + if (!$bIsFirst) { + // Had some output + $sResult .= $oOutputFormat->spaceAfterRules(); + } + + return $oOutputFormat->removeLastSemicolon($sResult); + } + + /** + * @param array<string, Comment> $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array<string, Comment> + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array<string, Comment> $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Settings.php b/vendor/sabberworm/php-css-parser/src/Settings.php new file mode 100644 index 0000000..7b85809 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Settings.php @@ -0,0 +1,89 @@ +<?php + +namespace Sabberworm\CSS; + +/** + * Parser settings class. + * + * Configure parser behaviour here. + */ +class Settings +{ + /** + * Multi-byte string support. + * If true (mbstring extension must be enabled), will use (slower) `mb_strlen`, `mb_convert_case`, `mb_substr` + * and `mb_strpos` functions. Otherwise, the normal (ASCII-Only) functions will be used. + * + * @var bool + */ + public $bMultibyteSupport; + + /** + * The default charset for the CSS if no `@charset` rule is found. Defaults to utf-8. + * + * @var string + */ + public $sDefaultCharset = 'utf-8'; + + /** + * Lenient parsing. When used (which is true by default), the parser will not choke + * on unexpected tokens but simply ignore them. + * + * @var bool + */ + public $bLenientParsing = true; + + private function __construct() + { + $this->bMultibyteSupport = extension_loaded('mbstring'); + } + + /** + * @return self new instance + */ + public static function create() + { + return new Settings(); + } + + /** + * @param bool $bMultibyteSupport + * + * @return self fluent interface + */ + public function withMultibyteSupport($bMultibyteSupport = true) + { + $this->bMultibyteSupport = $bMultibyteSupport; + return $this; + } + + /** + * @param string $sDefaultCharset + * + * @return self fluent interface + */ + public function withDefaultCharset($sDefaultCharset) + { + $this->sDefaultCharset = $sDefaultCharset; + return $this; + } + + /** + * @param bool $bLenientParsing + * + * @return self fluent interface + */ + public function withLenientParsing($bLenientParsing = true) + { + $this->bLenientParsing = $bLenientParsing; + return $this; + } + + /** + * @return self fluent interface + */ + public function beStrict() + { + return $this->withLenientParsing(false); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php b/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php new file mode 100644 index 0000000..e6b8c11 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php @@ -0,0 +1,73 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; + +class CSSFunction extends ValueList +{ + /** + * @var string + */ + protected $sName; + + /** + * @param string $sName + * @param RuleValueList|array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> $aArguments + * @param string $sSeparator + * @param int $iLineNo + */ + public function __construct($sName, $aArguments, $sSeparator = ',', $iLineNo = 0) + { + if ($aArguments instanceof RuleValueList) { + $sSeparator = $aArguments->getListSeparator(); + $aArguments = $aArguments->getListComponents(); + } + $this->sName = $sName; + $this->iLineNo = $iLineNo; + parent::__construct($aArguments, $sSeparator, $iLineNo); + } + + /** + * @return string + */ + public function getName() + { + return $this->sName; + } + + /** + * @param string $sName + * + * @return void + */ + public function setName($sName) + { + $this->sName = $sName; + } + + /** + * @return array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> + */ + public function getArguments() + { + return $this->aComponents; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $aArguments = parent::render($oOutputFormat); + return "{$this->sName}({$aArguments})"; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/CSSString.php b/vendor/sabberworm/php-css-parser/src/Value/CSSString.php new file mode 100644 index 0000000..9fafedd --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/CSSString.php @@ -0,0 +1,105 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\SourceException; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; + +class CSSString extends PrimitiveValue +{ + /** + * @var string + */ + private $sString; + + /** + * @param string $sString + * @param int $iLineNo + */ + public function __construct($sString, $iLineNo = 0) + { + $this->sString = $sString; + parent::__construct($iLineNo); + } + + /** + * @return CSSString + * + * @throws SourceException + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $sBegin = $oParserState->peek(); + $sQuote = null; + if ($sBegin === "'") { + $sQuote = "'"; + } elseif ($sBegin === '"') { + $sQuote = '"'; + } + if ($sQuote !== null) { + $oParserState->consume($sQuote); + } + $sResult = ""; + $sContent = null; + if ($sQuote === null) { + // Unquoted strings end in whitespace or with braces, brackets, parentheses + while (!preg_match('/[\\s{}()<>\\[\\]]/isu', $oParserState->peek())) { + $sResult .= $oParserState->parseCharacter(false); + } + } else { + while (!$oParserState->comes($sQuote)) { + $sContent = $oParserState->parseCharacter(false); + if ($sContent === null) { + throw new SourceException( + "Non-well-formed quoted string {$oParserState->peek(3)}", + $oParserState->currentLine() + ); + } + $sResult .= $sContent; + } + $oParserState->consume($sQuote); + } + return new CSSString($sResult, $oParserState->currentLine()); + } + + /** + * @param string $sString + * + * @return void + */ + public function setString($sString) + { + $this->sString = $sString; + } + + /** + * @return string + */ + public function getString() + { + return $this->sString; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sString = addslashes($this->sString); + $sString = str_replace("\n", '\A', $sString); + return $oOutputFormat->getStringQuotingType() . $sString . $oOutputFormat->getStringQuotingType(); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php b/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php new file mode 100644 index 0000000..5c92e0c --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php @@ -0,0 +1,89 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; + +class CalcFunction extends CSSFunction +{ + /** + * @var int + */ + const T_OPERAND = 1; + + /** + * @var int + */ + const T_OPERATOR = 2; + + /** + * @return CalcFunction + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parse(ParserState $oParserState) + { + $aOperators = ['+', '-', '*', '/']; + $sFunction = trim($oParserState->consumeUntil('(', false, true)); + $oCalcList = new CalcRuleValueList($oParserState->currentLine()); + $oList = new RuleValueList(',', $oParserState->currentLine()); + $iNestingLevel = 0; + $iLastComponentType = null; + while (!$oParserState->comes(')') || $iNestingLevel > 0) { + $oParserState->consumeWhiteSpace(); + if ($oParserState->comes('(')) { + $iNestingLevel++; + $oCalcList->addListComponent($oParserState->consume(1)); + $oParserState->consumeWhiteSpace(); + continue; + } elseif ($oParserState->comes(')')) { + $iNestingLevel--; + $oCalcList->addListComponent($oParserState->consume(1)); + $oParserState->consumeWhiteSpace(); + continue; + } + if ($iLastComponentType != CalcFunction::T_OPERAND) { + $oVal = Value::parsePrimitiveValue($oParserState); + $oCalcList->addListComponent($oVal); + $iLastComponentType = CalcFunction::T_OPERAND; + } else { + if (in_array($oParserState->peek(), $aOperators)) { + if (($oParserState->comes('-') || $oParserState->comes('+'))) { + if ( + $oParserState->peek(1, -1) != ' ' + || !($oParserState->comes('- ') + || $oParserState->comes('+ ')) + ) { + throw new UnexpectedTokenException( + " {$oParserState->peek()} ", + $oParserState->peek(1, -1) . $oParserState->peek(2), + 'literal', + $oParserState->currentLine() + ); + } + } + $oCalcList->addListComponent($oParserState->consume(1)); + $iLastComponentType = CalcFunction::T_OPERATOR; + } else { + throw new UnexpectedTokenException( + sprintf( + 'Next token was expected to be an operand of type %s. Instead "%s" was found.', + implode(', ', $aOperators), + $oVal + ), + '', + 'custom', + $oParserState->currentLine() + ); + } + } + $oParserState->consumeWhiteSpace(); + } + $oList->addListComponent($oCalcList); + $oParserState->consume(')'); + return new CalcFunction($sFunction, $oList, ',', $oParserState->currentLine()); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php b/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php new file mode 100644 index 0000000..7dbd26a --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php @@ -0,0 +1,24 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; + +class CalcRuleValueList extends RuleValueList +{ + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + parent::__construct(',', $iLineNo); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return $oOutputFormat->implode(' ', $this->aComponents); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/Color.php b/vendor/sabberworm/php-css-parser/src/Value/Color.php new file mode 100644 index 0000000..8dc5296 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/Color.php @@ -0,0 +1,166 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; + +class Color extends CSSFunction +{ + /** + * @param array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> $aColor + * @param int $iLineNo + */ + public function __construct(array $aColor, $iLineNo = 0) + { + parent::__construct(implode('', array_keys($aColor)), $aColor, ',', $iLineNo); + } + + /** + * @return Color|CSSFunction + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $aColor = []; + if ($oParserState->comes('#')) { + $oParserState->consume('#'); + $sValue = $oParserState->parseIdentifier(false); + if ($oParserState->strlen($sValue) === 3) { + $sValue = $sValue[0] . $sValue[0] . $sValue[1] . $sValue[1] . $sValue[2] . $sValue[2]; + } elseif ($oParserState->strlen($sValue) === 4) { + $sValue = $sValue[0] . $sValue[0] . $sValue[1] . $sValue[1] . $sValue[2] . $sValue[2] . $sValue[3] + . $sValue[3]; + } + + if ($oParserState->strlen($sValue) === 8) { + $aColor = [ + 'r' => new Size(intval($sValue[0] . $sValue[1], 16), null, true, $oParserState->currentLine()), + 'g' => new Size(intval($sValue[2] . $sValue[3], 16), null, true, $oParserState->currentLine()), + 'b' => new Size(intval($sValue[4] . $sValue[5], 16), null, true, $oParserState->currentLine()), + 'a' => new Size( + round(self::mapRange(intval($sValue[6] . $sValue[7], 16), 0, 255, 0, 1), 2), + null, + true, + $oParserState->currentLine() + ), + ]; + } else { + $aColor = [ + 'r' => new Size(intval($sValue[0] . $sValue[1], 16), null, true, $oParserState->currentLine()), + 'g' => new Size(intval($sValue[2] . $sValue[3], 16), null, true, $oParserState->currentLine()), + 'b' => new Size(intval($sValue[4] . $sValue[5], 16), null, true, $oParserState->currentLine()), + ]; + } + } else { + $sColorMode = $oParserState->parseIdentifier(true); + $oParserState->consumeWhiteSpace(); + $oParserState->consume('('); + + $bContainsVar = false; + $iLength = $oParserState->strlen($sColorMode); + for ($i = 0; $i < $iLength; ++$i) { + $oParserState->consumeWhiteSpace(); + if ($oParserState->comes('var')) { + $aColor[$sColorMode[$i]] = CSSFunction::parseIdentifierOrFunction($oParserState); + $bContainsVar = true; + } else { + $aColor[$sColorMode[$i]] = Size::parse($oParserState, true); + } + + if ($bContainsVar && $oParserState->comes(')')) { + // With a var argument the function can have fewer arguments + break; + } + + $oParserState->consumeWhiteSpace(); + if ($i < ($iLength - 1)) { + $oParserState->consume(','); + } + } + $oParserState->consume(')'); + + if ($bContainsVar) { + return new CSSFunction($sColorMode, array_values($aColor), ',', $oParserState->currentLine()); + } + } + return new Color($aColor, $oParserState->currentLine()); + } + + /** + * @param float $fVal + * @param float $fFromMin + * @param float $fFromMax + * @param float $fToMin + * @param float $fToMax + * + * @return float + */ + private static function mapRange($fVal, $fFromMin, $fFromMax, $fToMin, $fToMax) + { + $fFromRange = $fFromMax - $fFromMin; + $fToRange = $fToMax - $fToMin; + $fMultiplier = $fToRange / $fFromRange; + $fNewVal = $fVal - $fFromMin; + $fNewVal *= $fMultiplier; + return $fNewVal + $fToMin; + } + + /** + * @return array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> + */ + public function getColor() + { + return $this->aComponents; + } + + /** + * @param array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> $aColor + * + * @return void + */ + public function setColor(array $aColor) + { + $this->setName(implode('', array_keys($aColor))); + $this->aComponents = $aColor; + } + + /** + * @return string + */ + public function getColorDescription() + { + return $this->getName(); + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + // Shorthand RGB color values + if ($oOutputFormat->getRGBHashNotation() && implode('', array_keys($this->aComponents)) === 'rgb') { + $sResult = sprintf( + '%02x%02x%02x', + $this->aComponents['r']->getSize(), + $this->aComponents['g']->getSize(), + $this->aComponents['b']->getSize() + ); + return '#' . (($sResult[0] == $sResult[1]) && ($sResult[2] == $sResult[3]) && ($sResult[4] == $sResult[5]) + ? "$sResult[0]$sResult[2]$sResult[4]" : $sResult); + } + return parent::render($oOutputFormat); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/LineName.php b/vendor/sabberworm/php-css-parser/src/Value/LineName.php new file mode 100644 index 0000000..e231ce3 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/LineName.php @@ -0,0 +1,65 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; + +class LineName extends ValueList +{ + /** + * @param array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> $aComponents + * @param int $iLineNo + */ + public function __construct(array $aComponents = [], $iLineNo = 0) + { + parent::__construct($aComponents, ' ', $iLineNo); + } + + /** + * @return LineName + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parse(ParserState $oParserState) + { + $oParserState->consume('['); + $oParserState->consumeWhiteSpace(); + $aNames = []; + do { + if ($oParserState->getSettings()->bLenientParsing) { + try { + $aNames[] = $oParserState->parseIdentifier(); + } catch (UnexpectedTokenException $e) { + if (!$oParserState->comes(']')) { + throw $e; + } + } + } else { + $aNames[] = $oParserState->parseIdentifier(); + } + $oParserState->consumeWhiteSpace(); + } while (!$oParserState->comes(']')); + $oParserState->consume(']'); + return new LineName($aNames, $oParserState->currentLine()); + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return '[' . parent::render(OutputFormat::createCompact()) . ']'; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php b/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php new file mode 100644 index 0000000..055a439 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php @@ -0,0 +1,14 @@ +<?php + +namespace Sabberworm\CSS\Value; + +abstract class PrimitiveValue extends Value +{ + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + parent::__construct($iLineNo); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php b/vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php new file mode 100644 index 0000000..5d533a7 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php @@ -0,0 +1,15 @@ +<?php + +namespace Sabberworm\CSS\Value; + +class RuleValueList extends ValueList +{ + /** + * @param string $sSeparator + * @param int $iLineNo + */ + public function __construct($sSeparator = ',', $iLineNo = 0) + { + parent::__construct([], $sSeparator, $iLineNo); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/Size.php b/vendor/sabberworm/php-css-parser/src/Value/Size.php new file mode 100644 index 0000000..b3801dc --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/Size.php @@ -0,0 +1,209 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; + +class Size extends PrimitiveValue +{ + /** + * vh/vw/vm(ax)/vmin/rem are absolute insofar as they don’t scale to the immediate parent (only the viewport) + * + * @var array<int, string> + */ + const ABSOLUTE_SIZE_UNITS = ['px', 'cm', 'mm', 'mozmm', 'in', 'pt', 'pc', 'vh', 'vw', 'vmin', 'vmax', 'rem']; + + /** + * @var array<int, string> + */ + const RELATIVE_SIZE_UNITS = ['%', 'em', 'ex', 'ch', 'fr']; + + /** + * @var array<int, string> + */ + const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turns', 'Hz', 'kHz']; + + /** + * @var array<int, array<string, string>>|null + */ + private static $SIZE_UNITS = null; + + /** + * @var float + */ + private $fSize; + + /** + * @var string|null + */ + private $sUnit; + + /** + * @var bool + */ + private $bIsColorComponent; + + /** + * @param float|int|string $fSize + * @param string|null $sUnit + * @param bool $bIsColorComponent + * @param int $iLineNo + */ + public function __construct($fSize, $sUnit = null, $bIsColorComponent = false, $iLineNo = 0) + { + parent::__construct($iLineNo); + $this->fSize = (float)$fSize; + $this->sUnit = $sUnit; + $this->bIsColorComponent = $bIsColorComponent; + } + + /** + * @param bool $bIsColorComponent + * + * @return Size + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState, $bIsColorComponent = false) + { + $sSize = ''; + if ($oParserState->comes('-')) { + $sSize .= $oParserState->consume('-'); + } + while (is_numeric($oParserState->peek()) || $oParserState->comes('.')) { + if ($oParserState->comes('.')) { + $sSize .= $oParserState->consume('.'); + } else { + $sSize .= $oParserState->consume(1); + } + } + + $sUnit = null; + $aSizeUnits = self::getSizeUnits(); + foreach ($aSizeUnits as $iLength => &$aValues) { + $sKey = strtolower($oParserState->peek($iLength)); + if (array_key_exists($sKey, $aValues)) { + if (($sUnit = $aValues[$sKey]) !== null) { + $oParserState->consume($iLength); + break; + } + } + } + return new Size((float)$sSize, $sUnit, $bIsColorComponent, $oParserState->currentLine()); + } + + /** + * @return array<int, array<string, string>> + */ + private static function getSizeUnits() + { + if (!is_array(self::$SIZE_UNITS)) { + self::$SIZE_UNITS = []; + foreach (array_merge(self::ABSOLUTE_SIZE_UNITS, self::RELATIVE_SIZE_UNITS, self::NON_SIZE_UNITS) as $val) { + $iSize = strlen($val); + if (!isset(self::$SIZE_UNITS[$iSize])) { + self::$SIZE_UNITS[$iSize] = []; + } + self::$SIZE_UNITS[$iSize][strtolower($val)] = $val; + } + + krsort(self::$SIZE_UNITS, SORT_NUMERIC); + } + + return self::$SIZE_UNITS; + } + + /** + * @param string $sUnit + * + * @return void + */ + public function setUnit($sUnit) + { + $this->sUnit = $sUnit; + } + + /** + * @return string|null + */ + public function getUnit() + { + return $this->sUnit; + } + + /** + * @param float|int|string $fSize + */ + public function setSize($fSize) + { + $this->fSize = (float)$fSize; + } + + /** + * @return float + */ + public function getSize() + { + return $this->fSize; + } + + /** + * @return bool + */ + public function isColorComponent() + { + return $this->bIsColorComponent; + } + + /** + * Returns whether the number stored in this Size really represents a size (as in a length of something on screen). + * + * @return false if the unit an angle, a duration, a frequency or the number is a component in a Color object. + */ + public function isSize() + { + if (in_array($this->sUnit, self::NON_SIZE_UNITS, true)) { + return false; + } + return !$this->isColorComponent(); + } + + /** + * @return bool + */ + public function isRelative() + { + if (in_array($this->sUnit, self::RELATIVE_SIZE_UNITS, true)) { + return true; + } + if ($this->sUnit === null && $this->fSize != 0) { + return true; + } + return false; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $l = localeconv(); + $sPoint = preg_quote($l['decimal_point'], '/'); + $sSize = preg_match("/[\d\.]+e[+-]?\d+/i", (string)$this->fSize) + ? preg_replace("/$sPoint?0+$/", "", sprintf("%f", $this->fSize)) : $this->fSize; + return preg_replace(["/$sPoint/", "/^(-?)0\./"], ['.', '$1.'], $sSize) + . ($this->sUnit === null ? '' : $this->sUnit); + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/URL.php b/vendor/sabberworm/php-css-parser/src/Value/URL.php new file mode 100644 index 0000000..1467d50 --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/URL.php @@ -0,0 +1,82 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\SourceException; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; + +class URL extends PrimitiveValue +{ + /** + * @var CSSString + */ + private $oURL; + + /** + * @param int $iLineNo + */ + public function __construct(CSSString $oURL, $iLineNo = 0) + { + parent::__construct($iLineNo); + $this->oURL = $oURL; + } + + /** + * @return URL + * + * @throws SourceException + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $bUseUrl = $oParserState->comes('url', true); + if ($bUseUrl) { + $oParserState->consume('url'); + $oParserState->consumeWhiteSpace(); + $oParserState->consume('('); + } + $oParserState->consumeWhiteSpace(); + $oResult = new URL(CSSString::parse($oParserState), $oParserState->currentLine()); + if ($bUseUrl) { + $oParserState->consumeWhiteSpace(); + $oParserState->consume(')'); + } + return $oResult; + } + + /** + * @return void + */ + public function setURL(CSSString $oURL) + { + $this->oURL = $oURL; + } + + /** + * @return CSSString + */ + public function getURL() + { + return $this->oURL; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return "url({$this->oURL->render($oOutputFormat)})"; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/Value.php b/vendor/sabberworm/php-css-parser/src/Value/Value.php new file mode 100644 index 0000000..66cb9fd --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/Value.php @@ -0,0 +1,198 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Parsing\SourceException; +use Sabberworm\CSS\Parsing\UnexpectedEOFException; +use Sabberworm\CSS\Parsing\UnexpectedTokenException; +use Sabberworm\CSS\Renderable; + +abstract class Value implements Renderable +{ + /** + * @var int + */ + protected $iLineNo; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + $this->iLineNo = $iLineNo; + } + + /** + * @param array<array-key, string> $aListDelimiters + * + * @return RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parseValue(ParserState $oParserState, array $aListDelimiters = []) + { + /** @var array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> $aStack */ + $aStack = []; + $oParserState->consumeWhiteSpace(); + //Build a list of delimiters and parsed values + while ( + !($oParserState->comes('}') || $oParserState->comes(';') || $oParserState->comes('!') + || $oParserState->comes(')') + || $oParserState->comes('\\')) + ) { + if (count($aStack) > 0) { + $bFoundDelimiter = false; + foreach ($aListDelimiters as $sDelimiter) { + if ($oParserState->comes($sDelimiter)) { + array_push($aStack, $oParserState->consume($sDelimiter)); + $oParserState->consumeWhiteSpace(); + $bFoundDelimiter = true; + break; + } + } + if (!$bFoundDelimiter) { + //Whitespace was the list delimiter + array_push($aStack, ' '); + } + } + array_push($aStack, self::parsePrimitiveValue($oParserState)); + $oParserState->consumeWhiteSpace(); + } + // Convert the list to list objects + foreach ($aListDelimiters as $sDelimiter) { + if (count($aStack) === 1) { + return $aStack[0]; + } + $iStartPosition = null; + while (($iStartPosition = array_search($sDelimiter, $aStack, true)) !== false) { + $iLength = 2; //Number of elements to be joined + for ($i = $iStartPosition + 2; $i < count($aStack); $i += 2, ++$iLength) { + if ($sDelimiter !== $aStack[$i]) { + break; + } + } + $oList = new RuleValueList($sDelimiter, $oParserState->currentLine()); + for ($i = $iStartPosition - 1; $i - $iStartPosition + 1 < $iLength * 2; $i += 2) { + $oList->addListComponent($aStack[$i]); + } + array_splice($aStack, $iStartPosition - 1, $iLength * 2 - 1, [$oList]); + } + } + if (!isset($aStack[0])) { + throw new UnexpectedTokenException( + " {$oParserState->peek()} ", + $oParserState->peek(1, -1) . $oParserState->peek(2), + 'literal', + $oParserState->currentLine() + ); + } + return $aStack[0]; + } + + /** + * @param bool $bIgnoreCase + * + * @return CSSFunction|string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false) + { + $sResult = $oParserState->parseIdentifier($bIgnoreCase); + + if ($oParserState->comes('(')) { + $oParserState->consume('('); + $aArguments = Value::parseValue($oParserState, ['=', ' ', ',']); + $sResult = new CSSFunction($sResult, $aArguments, ',', $oParserState->currentLine()); + $oParserState->consume(')'); + } + + return $sResult; + } + + /** + * @return CSSFunction|CSSString|LineName|Size|URL|string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + * @throws SourceException + */ + public static function parsePrimitiveValue(ParserState $oParserState) + { + $oValue = null; + $oParserState->consumeWhiteSpace(); + if ( + is_numeric($oParserState->peek()) + || ($oParserState->comes('-.') + && is_numeric($oParserState->peek(1, 2))) + || (($oParserState->comes('-') || $oParserState->comes('.')) && is_numeric($oParserState->peek(1, 1))) + ) { + $oValue = Size::parse($oParserState); + } elseif ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) { + $oValue = Color::parse($oParserState); + } elseif ($oParserState->comes('url', true)) { + $oValue = URL::parse($oParserState); + } elseif ( + $oParserState->comes('calc', true) || $oParserState->comes('-webkit-calc', true) + || $oParserState->comes('-moz-calc', true) + ) { + $oValue = CalcFunction::parse($oParserState); + } elseif ($oParserState->comes("'") || $oParserState->comes('"')) { + $oValue = CSSString::parse($oParserState); + } elseif ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) { + $oValue = self::parseMicrosoftFilter($oParserState); + } elseif ($oParserState->comes("[")) { + $oValue = LineName::parse($oParserState); + } elseif ($oParserState->comes("U+")) { + $oValue = self::parseUnicodeRangeValue($oParserState); + } else { + $oValue = self::parseIdentifierOrFunction($oParserState); + } + $oParserState->consumeWhiteSpace(); + return $oValue; + } + + /** + * @return CSSFunction + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + private static function parseMicrosoftFilter(ParserState $oParserState) + { + $sFunction = $oParserState->consumeUntil('(', false, true); + $aArguments = Value::parseValue($oParserState, [',', '=']); + return new CSSFunction($sFunction, $aArguments, ',', $oParserState->currentLine()); + } + + /** + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + private static function parseUnicodeRangeValue(ParserState $oParserState) + { + $iCodepointMaxLength = 6; // Code points outside BMP can use up to six digits + $sRange = ""; + $oParserState->consume("U+"); + do { + if ($oParserState->comes('-')) { + $iCodepointMaxLength = 13; // Max length is 2 six digit code points + the dash(-) between them + } + $sRange .= $oParserState->consume(1); + } while (strlen($sRange) < $iCodepointMaxLength && preg_match("/[A-Fa-f0-9\?-]/", $oParserState->peek())); + return "U+{$sRange}"; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } +} diff --git a/vendor/sabberworm/php-css-parser/src/Value/ValueList.php b/vendor/sabberworm/php-css-parser/src/Value/ValueList.php new file mode 100644 index 0000000..af5348b --- /dev/null +++ b/vendor/sabberworm/php-css-parser/src/Value/ValueList.php @@ -0,0 +1,100 @@ +<?php + +namespace Sabberworm\CSS\Value; + +use Sabberworm\CSS\OutputFormat; + +abstract class ValueList extends Value +{ + /** + * @var array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> + */ + protected $aComponents; + + /** + * @var string + */ + protected $sSeparator; + + /** + * phpcs:ignore Generic.Files.LineLength + * @param array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string>|RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string $aComponents + * @param string $sSeparator + * @param int $iLineNo + */ + public function __construct($aComponents = [], $sSeparator = ',', $iLineNo = 0) + { + parent::__construct($iLineNo); + if (!is_array($aComponents)) { + $aComponents = [$aComponents]; + } + $this->aComponents = $aComponents; + $this->sSeparator = $sSeparator; + } + + /** + * @param RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string $mComponent + * + * @return void + */ + public function addListComponent($mComponent) + { + $this->aComponents[] = $mComponent; + } + + /** + * @return array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> + */ + public function getListComponents() + { + return $this->aComponents; + } + + /** + * @param array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> $aComponents + * + * @return void + */ + public function setListComponents(array $aComponents) + { + $this->aComponents = $aComponents; + } + + /** + * @return string + */ + public function getListSeparator() + { + return $this->sSeparator; + } + + /** + * @param string $sSeparator + * + * @return void + */ + public function setListSeparator($sSeparator) + { + $this->sSeparator = $sSeparator; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return $oOutputFormat->implode( + $oOutputFormat->spaceBeforeListArgumentSeparator($this->sSeparator) . $this->sSeparator + . $oOutputFormat->spaceAfterListArgumentSeparator($this->sSeparator), + $this->aComponents + ); + } +} |