<?php
namespace MailPoetVendor\Sabberworm\CSS\RuleSet;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Sabberworm\CSS\CSSList\CSSList;
use MailPoetVendor\Sabberworm\CSS\CSSList\KeyFrame;
use MailPoetVendor\Sabberworm\CSS\OutputFormat;
use MailPoetVendor\Sabberworm\CSS\Parsing\OutputException;
use MailPoetVendor\Sabberworm\CSS\Parsing\ParserState;
use MailPoetVendor\Sabberworm\CSS\Parsing\UnexpectedEOFException;
use MailPoetVendor\Sabberworm\CSS\Parsing\UnexpectedTokenException;
use MailPoetVendor\Sabberworm\CSS\Property\KeyframeSelector;
use MailPoetVendor\Sabberworm\CSS\Property\Selector;
use MailPoetVendor\Sabberworm\CSS\Rule\Rule;
use MailPoetVendor\Sabberworm\CSS\Value\Color;
use MailPoetVendor\Sabberworm\CSS\Value\RuleValueList;
use MailPoetVendor\Sabberworm\CSS\Value\Size;
use MailPoetVendor\Sabberworm\CSS\Value\URL;
use MailPoetVendor\Sabberworm\CSS\Value\Value;
class DeclarationBlock extends RuleSet
{
 private $aSelectors;
 public function __construct($iLineNo = 0)
 {
 parent::__construct($iLineNo);
 $this->aSelectors = [];
 }
 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;
 }
 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);
 }
 }
 }
 }
 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;
 }
 public function getSelector()
 {
 return $this->getSelectors();
 }
 public function setSelector($mSelector, $oList = null)
 {
 $this->setSelectors($mSelector, $oList);
 }
 public function getSelectors()
 {
 return $this->aSelectors;
 }
 public function expandShorthands()
 {
 // border must be expanded before dimensions
 $this->expandBorderShorthand();
 $this->expandDimensionsShorthand();
 $this->expandFontShorthand();
 $this->expandBackgroundShorthand();
 $this->expandListStyleShorthand();
 }
 public function createShorthands()
 {
 $this->createBackgroundShorthand();
 $this->createDimensionsShorthand();
 // border must be shortened after dimensions
 $this->createBorderShorthand();
 $this->createFontShorthand();
 $this->createListStyleShorthand();
 }
 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);
 }
 }
 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);
 }
 }
 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');
 }
 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');
 }
 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');
 }
 public function createShorthandProperties(array $aProperties, $sShorthand)
 {
 $aRules = $this->getRulesAssoc();
 $oRule = null;
 $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 ($aNewValues !== [] && $oRule instanceof Rule) {
 $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo());
 foreach ($aNewValues as $mValue) {
 $oNewRule->addValue($mValue);
 }
 $this->addRule($oNewRule);
 }
 }
 public function createBackgroundShorthand()
 {
 $aProperties = ['background-color', 'background-image', 'background-repeat', 'background-position', 'background-attachment'];
 $this->createShorthandProperties($aProperties, 'background');
 }
 public function createListStyleShorthand()
 {
 $aProperties = ['list-style-type', 'list-style-position', 'list-style-image'];
 $this->createShorthandProperties($aProperties, 'list-style');
 }
 public function createBorderShorthand()
 {
 $aProperties = ['border-width', 'border-style', 'border-color'];
 $this->createShorthandProperties($aProperties, 'border');
 }
 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));
 }
 }
 }
 }
 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);
 }
 }
 public function __toString()
 {
 return $this->render(new OutputFormat());
 }
 public function render(OutputFormat $oOutputFormat)
 {
 $sResult = $oOutputFormat->comments($this);
 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 .= $this->renderRules($oOutputFormat);
 $sResult .= '}';
 $sResult .= $oOutputFormat->sAfterDeclarationBlock;
 return $sResult;
 }
}
