<?php
namespace Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\RuleSet;
if (!defined('ABSPATH')) exit;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\CSSList\CSSList;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\CSSList\KeyFrame;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\OutputFormat;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Parsing\OutputException;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Parsing\ParserState;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Parsing\UnexpectedEOFException;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Parsing\UnexpectedTokenException;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Property\KeyframeSelector;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Property\Selector;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Rule\Rule;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Value\Color;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Value\RuleValueList;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Value\Size;
use Automattic\WooCommerce\EmailEditorVendor\Sabberworm\CSS\Value\URL;
use Automattic\WooCommerce\EmailEditorVendor\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, '%', false, $this->getLineNo()),
 new Size(0, '%', false, $this->getLineNo()),
 ],
 ];
 $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->getLineNo());
 $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->getLineNo());
 $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($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->getLineNumber()
 );
 }
 $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;
 }
}
