<?php namespace MailPoetVendor\Sabberworm\CSS\RuleSet; if (!defined('ABSPATH')) exit; use MailPoetVendor\Sabberworm\CSS\Comment\Comment; use MailPoetVendor\Sabberworm\CSS\Comment\Commentable; use MailPoetVendor\Sabberworm\CSS\OutputFormat; use MailPoetVendor\Sabberworm\CSS\Parsing\ParserState; use MailPoetVendor\Sabberworm\CSS\Parsing\UnexpectedEOFException; use MailPoetVendor\Sabberworm\CSS\Parsing\UnexpectedTokenException; use MailPoetVendor\Sabberworm\CSS\Renderable; use MailPoetVendor\Sabberworm\CSS\Rule\Rule; abstract class RuleSet implements Renderable, Commentable { private $aRules; protected $iLineNo; protected $aComments; public function __construct($iLineNo = 0) { $this->aRules = []; $this->iLineNo = $iLineNo; $this->aComments = []; } 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('}'); } public function getLineNo() { return $this->iLineNo; } 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]); } public function getRules($mRule = null) { if ($mRule instanceof Rule) { $mRule = $mRule->getRule(); } $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; } public function setRules(array $aRules) { $this->aRules = []; foreach ($aRules as $rule) { $this->addRule($rule); } } public function getRulesAssoc($mRule = null) { $aResult = []; foreach ($this->getRules($mRule) as $oRule) { $aResult[$oRule->getRule()] = $oRule; } return $aResult; } 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]); } } } } public function __toString() { return $this->render(new OutputFormat()); } 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); } public function addComments(array $aComments) { $this->aComments = \array_merge($this->aComments, $aComments); } public function getComments() { return $this->aComments; } public function setComments(array $aComments) { $this->aComments = $aComments; } }