PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.1
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
← All changes | back/Libraries/Sabberworm/CSS/src/RuleSet/DeclarationBlock.php +0 -831 10.2.2 → 11.1.1 View file →
@@ -1,831 +1,0 @@
1 -<?php
2 -
3 -namespace Sabberworm\CSS\RuleSet;
4 -
5 -use Sabberworm\CSS\CSSList\CSSList;
6 -use Sabberworm\CSS\CSSList\KeyFrame;
7 -use Sabberworm\CSS\OutputFormat;
8 -use Sabberworm\CSS\Parsing\OutputException;
9 -use Sabberworm\CSS\Parsing\ParserState;
10 -use Sabberworm\CSS\Parsing\UnexpectedEOFException;
11 -use Sabberworm\CSS\Parsing\UnexpectedTokenException;
12 -use Sabberworm\CSS\Property\KeyframeSelector;
13 -use Sabberworm\CSS\Property\Selector;
14 -use Sabberworm\CSS\Rule\Rule;
15 -use Sabberworm\CSS\Value\Color;
16 -use Sabberworm\CSS\Value\RuleValueList;
17 -use Sabberworm\CSS\Value\Size;
18 -use Sabberworm\CSS\Value\URL;
19 -use Sabberworm\CSS\Value\Value;
20 -
21 -/**
22 - * Declaration blocks are the parts of a CSS file which denote the rules belonging to a selector.
23 - *
24 - * Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
25 - */
26 -class DeclarationBlock extends RuleSet
27 -{
28 - /**
29 - * @var array<int, Selector|string>
30 - */
31 - private $aSelectors;
32 -
33 - /**
34 - * @param int $iLineNo
35 - */
36 - public function __construct($iLineNo = 0)
37 - {
38 - parent::__construct($iLineNo);
39 - $this->aSelectors = [];
40 - }
41 -
42 - /**
43 - * @param CSSList|null $oList
44 - *
45 - * @return DeclarationBlock|false
46 - *
47 - * @throws UnexpectedTokenException
48 - * @throws UnexpectedEOFException
49 - */
50 - public static function parse(ParserState $oParserState, $oList = null)
51 - {
52 - $aComments = [];
53 - $oResult = new DeclarationBlock($oParserState->currentLine());
54 - try {
55 - $aSelectorParts = [];
56 - $sStringWrapperChar = false;
57 - do {
58 - $aSelectorParts[] = $oParserState->consume(1)
59 - . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments);
60 - if (in_array($oParserState->peek(), ['\'', '"']) && substr(end($aSelectorParts), -1) != "\\") {
61 - if ($sStringWrapperChar === false) {
62 - $sStringWrapperChar = $oParserState->peek();
63 - } elseif ($sStringWrapperChar == $oParserState->peek()) {
64 - $sStringWrapperChar = false;
65 - }
66 - }
67 - } while (!in_array($oParserState->peek(), ['{', '}']) || $sStringWrapperChar !== false);
68 - $oResult->setSelectors(implode('', $aSelectorParts), $oList);
69 - if ($oParserState->comes('{')) {
70 - $oParserState->consume(1);
71 - }
72 - } catch (UnexpectedTokenException $e) {
73 - if ($oParserState->getSettings()->bLenientParsing) {
74 - if (!$oParserState->comes('}')) {
75 - $oParserState->consumeUntil('}', false, true);
76 - }
77 - return false;
78 - } else {
79 - throw $e;
80 - }
81 - }
82 - $oResult->setComments($aComments);
83 - RuleSet::parseRuleSet($oParserState, $oResult);
84 - return $oResult;
85 - }
86 -
87 - /**
88 - * @param array<int, Selector|string>|string $mSelector
89 - * @param CSSList|null $oList
90 - *
91 - * @throws UnexpectedTokenException
92 - */
93 - public function setSelectors($mSelector, $oList = null)
94 - {
95 - if (is_array($mSelector)) {
96 - $this->aSelectors = $mSelector;
97 - } else {
98 - $this->aSelectors = explode(',', $mSelector);
99 - }
100 - foreach ($this->aSelectors as $iKey => $mSelector) {
101 - if (!($mSelector instanceof Selector)) {
102 - if ($oList === null || !($oList instanceof KeyFrame)) {
103 - if (!Selector::isValid($mSelector)) {
104 - throw new UnexpectedTokenException(
105 - "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
106 - $mSelector,
107 - "custom"
108 - );
109 - }
110 - $this->aSelectors[$iKey] = new Selector($mSelector);
111 - } else {
112 - if (!KeyframeSelector::isValid($mSelector)) {
113 - throw new UnexpectedTokenException(
114 - "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.",
115 - $mSelector,
116 - "custom"
117 - );
118 - }
119 - $this->aSelectors[$iKey] = new KeyframeSelector($mSelector);
120 - }
121 - }
122 - }
123 - }
124 -
125 - /**
126 - * Remove one of the selectors of the block.
127 - *
128 - * @param Selector|string $mSelector
129 - *
130 - * @return bool
131 - */
132 - public function removeSelector($mSelector)
133 - {
134 - if ($mSelector instanceof Selector) {
135 - $mSelector = $mSelector->getSelector();
136 - }
137 - foreach ($this->aSelectors as $iKey => $oSelector) {
138 - if ($oSelector->getSelector() === $mSelector) {
139 - unset($this->aSelectors[$iKey]);
140 - return true;
141 - }
142 - }
143 - return false;
144 - }
145 -
146 - /**
147 - * @return array<int, Selector|string>
148 - *
149 - * @deprecated will be removed in version 9.0; use `getSelectors()` instead
150 - */
151 - public function getSelector()
152 - {
153 - return $this->getSelectors();
154 - }
155 -
156 - /**
157 - * @param Selector|string $mSelector
158 - * @param CSSList|null $oList
159 - *
160 - * @return void
161 - *
162 - * @deprecated will be removed in version 9.0; use `setSelectors()` instead
163 - */
164 - public function setSelector($mSelector, $oList = null)
165 - {
166 - $this->setSelectors($mSelector, $oList);
167 - }
168 -
169 - /**
170 - * @return array<int, Selector|string>
171 - */
172 - public function getSelectors()
173 - {
174 - return $this->aSelectors;
175 - }
176 -
177 - /**
178 - * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts.
179 - *
180 - * @return void
181 - */
182 - public function expandShorthands()
183 - {
184 - // border must be expanded before dimensions
185 - $this->expandBorderShorthand();
186 - $this->expandDimensionsShorthand();
187 - $this->expandFontShorthand();
188 - $this->expandBackgroundShorthand();
189 - $this->expandListStyleShorthand();
190 - }
191 -
192 - /**
193 - * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible.
194 - *
195 - * @return void
196 - */
197 - public function createShorthands()
198 - {
199 - $this->createBackgroundShorthand();
200 - $this->createDimensionsShorthand();
201 - // border must be shortened after dimensions
202 - $this->createBorderShorthand();
203 - $this->createFontShorthand();
204 - $this->createListStyleShorthand();
205 - }
206 -
207 - /**
208 - * Splits shorthand border declarations (e.g. `border: 1px red;`).
209 - *
210 - * Additional splitting happens in expandDimensionsShorthand.
211 - *
212 - * Multiple borders are not yet supported as of 3.
213 - *
214 - * @return void
215 - */
216 - public function expandBorderShorthand()
217 - {
218 - $aBorderRules = [
219 - 'border',
220 - 'border-left',
221 - 'border-right',
222 - 'border-top',
223 - 'border-bottom',
224 - ];
225 - $aBorderSizes = [
226 - 'thin',
227 - 'medium',
228 - 'thick',
229 - ];
230 - $aRules = $this->getRulesAssoc();
231 - foreach ($aBorderRules as $sBorderRule) {
232 - if (!isset($aRules[$sBorderRule])) {
233 - continue;
234 - }
235 - $oRule = $aRules[$sBorderRule];
236 - $mRuleValue = $oRule->getValue();
237 - $aValues = [];
238 - if (!$mRuleValue instanceof RuleValueList) {
239 - $aValues[] = $mRuleValue;
240 - } else {
241 - $aValues = $mRuleValue->getListComponents();
242 - }
243 - foreach ($aValues as $mValue) {
244 - if ($mValue instanceof Value) {
245 - $mNewValue = clone $mValue;
246 - } else {
247 - $mNewValue = $mValue;
248 - }
249 - if ($mValue instanceof Size) {
250 - $sNewRuleName = $sBorderRule . "-width";
251 - } elseif ($mValue instanceof Color) {
252 - $sNewRuleName = $sBorderRule . "-color";
253 - } else {
254 - if (in_array($mValue, $aBorderSizes)) {
255 - $sNewRuleName = $sBorderRule . "-width";
256 - } else {
257 - $sNewRuleName = $sBorderRule . "-style";
258 - }
259 - }
260 - $oNewRule = new Rule($sNewRuleName, $oRule->getLineNo(), $oRule->getColNo());
261 - $oNewRule->setIsImportant($oRule->getIsImportant());
262 - $oNewRule->addValue([$mNewValue]);
263 - $this->addRule($oNewRule);
264 - }
265 - $this->removeRule($sBorderRule);
266 - }
267 - }
268 -
269 - /**
270 - * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`)
271 - * into their constituent parts.
272 - *
273 - * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`.
274 - *
275 - * @return void
276 - */
277 - public function expandDimensionsShorthand()
278 - {
279 - $aExpansions = [
280 - 'margin' => 'margin-%s',
281 - 'padding' => 'padding-%s',
282 - 'border-color' => 'border-%s-color',
283 - 'border-style' => 'border-%s-style',
284 - 'border-width' => 'border-%s-width',
285 - ];
286 - $aRules = $this->getRulesAssoc();
287 - foreach ($aExpansions as $sProperty => $sExpanded) {
288 - if (!isset($aRules[$sProperty])) {
289 - continue;
290 - }
291 - $oRule = $aRules[$sProperty];
292 - $mRuleValue = $oRule->getValue();
293 - $aValues = [];
294 - if (!$mRuleValue instanceof RuleValueList) {
295 - $aValues[] = $mRuleValue;
296 - } else {
297 - $aValues = $mRuleValue->getListComponents();
298 - }
299 - $top = $right = $bottom = $left = null;
300 - switch (count($aValues)) {
301 - case 1:
302 - $top = $right = $bottom = $left = $aValues[0];
303 - break;
304 - case 2:
305 - $top = $bottom = $aValues[0];
306 - $left = $right = $aValues[1];
307 - break;
308 - case 3:
309 - $top = $aValues[0];
310 - $left = $right = $aValues[1];
311 - $bottom = $aValues[2];
312 - break;
313 - case 4:
314 - $top = $aValues[0];
315 - $right = $aValues[1];
316 - $bottom = $aValues[2];
317 - $left = $aValues[3];
318 - break;
319 - }
320 - foreach (['top', 'right', 'bottom', 'left'] as $sPosition) {
321 - $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $oRule->getLineNo(), $oRule->getColNo());
322 - $oNewRule->setIsImportant($oRule->getIsImportant());
323 - $oNewRule->addValue(${$sPosition});
324 - $this->addRule($oNewRule);
325 - }
326 - $this->removeRule($sProperty);
327 - }
328 - }
329 -
330 - /**
331 - * Converts shorthand font declarations
332 - * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`)
333 - * into their constituent parts.
334 - *
335 - * @return void
336 - */
337 - public function expandFontShorthand()
338 - {
339 - $aRules = $this->getRulesAssoc();
340 - if (!isset($aRules['font'])) {
341 - return;
342 - }
343 - $oRule = $aRules['font'];
344 - // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand
345 - $aFontProperties = [
346 - 'font-style' => 'normal',
347 - 'font-variant' => 'normal',
348 - 'font-weight' => 'normal',
349 - 'font-size' => 'normal',
350 - 'line-height' => 'normal',
351 - ];
352 - $mRuleValue = $oRule->getValue();
353 - $aValues = [];
354 - if (!$mRuleValue instanceof RuleValueList) {
355 - $aValues[] = $mRuleValue;
356 - } else {
357 - $aValues = $mRuleValue->getListComponents();
358 - }
359 - foreach ($aValues as $mValue) {
360 - if (!$mValue instanceof Value) {
361 - $mValue = mb_strtolower($mValue);
362 - }
363 - if (in_array($mValue, ['normal', 'inherit'])) {
364 - foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) {
365 - if (!isset($aFontProperties[$sProperty])) {
366 - $aFontProperties[$sProperty] = $mValue;
367 - }
368 - }
369 - } elseif (in_array($mValue, ['italic', 'oblique'])) {
370 - $aFontProperties['font-style'] = $mValue;
371 - } elseif ($mValue == 'small-caps') {
372 - $aFontProperties['font-variant'] = $mValue;
373 - } elseif (
374 - in_array($mValue, ['bold', 'bolder', 'lighter'])
375 - || ($mValue instanceof Size
376 - && in_array($mValue->getSize(), range(100, 900, 100)))
377 - ) {
378 - $aFontProperties['font-weight'] = $mValue;
379 - } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') {
380 - list($oSize, $oHeight) = $mValue->getListComponents();
381 - $aFontProperties['font-size'] = $oSize;
382 - $aFontProperties['line-height'] = $oHeight;
383 - } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) {
384 - $aFontProperties['font-size'] = $mValue;
385 - } else {
386 - $aFontProperties['font-family'] = $mValue;
387 - }
388 - }
389 - foreach ($aFontProperties as $sProperty => $mValue) {
390 - $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
391 - $oNewRule->addValue($mValue);
392 - $oNewRule->setIsImportant($oRule->getIsImportant());
393 - $this->addRule($oNewRule);
394 - }
395 - $this->removeRule('font');
396 - }
397 -
398 - /**
399 - * Converts shorthand background declarations
400 - * (e.g. `background: url("chess.png") gray 50% repeat fixed;`)
401 - * into their constituent parts.
402 - *
403 - * @see http://www.w3.org/TR/21/colors.html#propdef-background
404 - *
405 - * @return void
406 - */
407 - public function expandBackgroundShorthand()
408 - {
409 - $aRules = $this->getRulesAssoc();
410 - if (!isset($aRules['background'])) {
411 - return;
412 - }
413 - $oRule = $aRules['background'];
414 - $aBgProperties = [
415 - 'background-color' => ['transparent'],
416 - 'background-image' => ['none'],
417 - 'background-repeat' => ['repeat'],
418 - 'background-attachment' => ['scroll'],
419 - 'background-position' => [
420 - new Size(0, '%', null, false, $this->iLineNo),
421 - new Size(0, '%', null, false, $this->iLineNo),
422 - ],
423 - ];
424 - $mRuleValue = $oRule->getValue();
425 - $aValues = [];
426 - if (!$mRuleValue instanceof RuleValueList) {
427 - $aValues[] = $mRuleValue;
428 - } else {
429 - $aValues = $mRuleValue->getListComponents();
430 - }
431 - if (count($aValues) == 1 && $aValues[0] == 'inherit') {
432 - foreach ($aBgProperties as $sProperty => $mValue) {
433 - $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
434 - $oNewRule->addValue('inherit');
435 - $oNewRule->setIsImportant($oRule->getIsImportant());
436 - $this->addRule($oNewRule);
437 - }
438 - $this->removeRule('background');
439 - return;
440 - }
441 - $iNumBgPos = 0;
442 - foreach ($aValues as $mValue) {
443 - if (!$mValue instanceof Value) {
444 - $mValue = mb_strtolower($mValue);
445 - }
446 - if ($mValue instanceof URL) {
447 - $aBgProperties['background-image'] = $mValue;
448 - } elseif ($mValue instanceof Color) {
449 - $aBgProperties['background-color'] = $mValue;
450 - } elseif (in_array($mValue, ['scroll', 'fixed'])) {
451 - $aBgProperties['background-attachment'] = $mValue;
452 - } elseif (in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'])) {
453 - $aBgProperties['background-repeat'] = $mValue;
454 - } elseif (
455 - in_array($mValue, ['left', 'center', 'right', 'top', 'bottom'])
456 - || $mValue instanceof Size
457 - ) {
458 - if ($iNumBgPos == 0) {
459 - $aBgProperties['background-position'][0] = $mValue;
460 - $aBgProperties['background-position'][1] = 'center';
461 - } else {
462 - $aBgProperties['background-position'][$iNumBgPos] = $mValue;
463 - }
464 - $iNumBgPos++;
465 - }
466 - }
467 - foreach ($aBgProperties as $sProperty => $mValue) {
468 - $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
469 - $oNewRule->setIsImportant($oRule->getIsImportant());
470 - $oNewRule->addValue($mValue);
471 - $this->addRule($oNewRule);
472 - }
473 - $this->removeRule('background');
474 - }
475 -
476 - /**
477 - * @return void
478 - */
479 - public function expandListStyleShorthand()
480 - {
481 - $aListProperties = [
482 - 'list-style-type' => 'disc',
483 - 'list-style-position' => 'outside',
484 - 'list-style-image' => 'none',
485 - ];
486 - $aListStyleTypes = [
487 - 'none',
488 - 'disc',
489 - 'circle',
490 - 'square',
491 - 'decimal-leading-zero',
492 - 'decimal',
493 - 'lower-roman',
494 - 'upper-roman',
495 - 'lower-greek',
496 - 'lower-alpha',
497 - 'lower-latin',
498 - 'upper-alpha',
499 - 'upper-latin',
500 - 'hebrew',
501 - 'armenian',
502 - 'georgian',
503 - 'cjk-ideographic',
504 - 'hiragana',
505 - 'hira-gana-iroha',
506 - 'katakana-iroha',
507 - 'katakana',
508 - ];
509 - $aListStylePositions = [
510 - 'inside',
511 - 'outside',
512 - ];
513 - $aRules = $this->getRulesAssoc();
514 - if (!isset($aRules['list-style'])) {
515 - return;
516 - }
517 - $oRule = $aRules['list-style'];
518 - $mRuleValue = $oRule->getValue();
519 - $aValues = [];
520 - if (!$mRuleValue instanceof RuleValueList) {
521 - $aValues[] = $mRuleValue;
522 - } else {
523 - $aValues = $mRuleValue->getListComponents();
524 - }
525 - if (count($aValues) == 1 && $aValues[0] == 'inherit') {
526 - foreach ($aListProperties as $sProperty => $mValue) {
527 - $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
528 - $oNewRule->addValue('inherit');
529 - $oNewRule->setIsImportant($oRule->getIsImportant());
530 - $this->addRule($oNewRule);
531 - }
532 - $this->removeRule('list-style');
533 - return;
534 - }
535 - foreach ($aValues as $mValue) {
536 - if (!$mValue instanceof Value) {
537 - $mValue = mb_strtolower($mValue);
538 - }
539 - if ($mValue instanceof Url) {
540 - $aListProperties['list-style-image'] = $mValue;
541 - } elseif (in_array($mValue, $aListStyleTypes)) {
542 - $aListProperties['list-style-types'] = $mValue;
543 - } elseif (in_array($mValue, $aListStylePositions)) {
544 - $aListProperties['list-style-position'] = $mValue;
545 - }
546 - }
547 - foreach ($aListProperties as $sProperty => $mValue) {
548 - $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
549 - $oNewRule->setIsImportant($oRule->getIsImportant());
550 - $oNewRule->addValue($mValue);
551 - $this->addRule($oNewRule);
552 - }
553 - $this->removeRule('list-style');
554 - }
555 -
556 - /**
557 - * @param array<array-key, string> $aProperties
558 - * @param string $sShorthand
559 - *
560 - * @return void
561 - */
562 - public function createShorthandProperties(array $aProperties, $sShorthand)
563 - {
564 - $aRules = $this->getRulesAssoc();
565 - $aNewValues = [];
566 - foreach ($aProperties as $sProperty) {
567 - if (!isset($aRules[$sProperty])) {
568 - continue;
569 - }
570 - $oRule = $aRules[$sProperty];
571 - if (!$oRule->getIsImportant()) {
572 - $mRuleValue = $oRule->getValue();
573 - $aValues = [];
574 - if (!$mRuleValue instanceof RuleValueList) {
575 - $aValues[] = $mRuleValue;
576 - } else {
577 - $aValues = $mRuleValue->getListComponents();
578 - }
579 - foreach ($aValues as $mValue) {
580 - $aNewValues[] = $mValue;
581 - }
582 - $this->removeRule($sProperty);
583 - }
584 - }
585 - if (count($aNewValues)) {
586 - $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo());
587 - foreach ($aNewValues as $mValue) {
588 - $oNewRule->addValue($mValue);
589 - }
590 - $this->addRule($oNewRule);
591 - }
592 - }
593 -
594 - /**
595 - * @return void
596 - */
597 - public function createBackgroundShorthand()
598 - {
599 - $aProperties = [
600 - 'background-color',
601 - 'background-image',
602 - 'background-repeat',
603 - 'background-position',
604 - 'background-attachment',
605 - ];
606 - $this->createShorthandProperties($aProperties, 'background');
607 - }
608 -
609 - /**
610 - * @return void
611 - */
612 - public function createListStyleShorthand()
613 - {
614 - $aProperties = [
615 - 'list-style-type',
616 - 'list-style-position',
617 - 'list-style-image',
618 - ];
619 - $this->createShorthandProperties($aProperties, 'list-style');
620 - }
621 -
622 - /**
623 - * Combines `border-color`, `border-style` and `border-width` into `border`.
624 - *
625 - * Should be run after `create_dimensions_shorthand`!
626 - *
627 - * @return void
628 - */
629 - public function createBorderShorthand()
630 - {
631 - $aProperties = [
632 - 'border-width',
633 - 'border-style',
634 - 'border-color',
635 - ];
636 - $this->createShorthandProperties($aProperties, 'border');
637 - }
638 -
639 - /**
640 - * Looks for long format CSS dimensional properties
641 - * (margin, padding, border-color, border-style and border-width)
642 - * and converts them into shorthand CSS properties.
643 - *
644 - * @return void
645 - */
646 - public function createDimensionsShorthand()
647 - {
648 - $aPositions = ['top', 'right', 'bottom', 'left'];
649 - $aExpansions = [
650 - 'margin' => 'margin-%s',
651 - 'padding' => 'padding-%s',
652 - 'border-color' => 'border-%s-color',
653 - 'border-style' => 'border-%s-style',
654 - 'border-width' => 'border-%s-width',
655 - ];
656 - $aRules = $this->getRulesAssoc();
657 - foreach ($aExpansions as $sProperty => $sExpanded) {
658 - $aFoldable = [];
659 - foreach ($aRules as $sRuleName => $oRule) {
660 - foreach ($aPositions as $sPosition) {
661 - if ($sRuleName == sprintf($sExpanded, $sPosition)) {
662 - $aFoldable[$sRuleName] = $oRule;
663 - }
664 - }
665 - }
666 - // All four dimensions must be present
667 - if (count($aFoldable) == 4) {
668 - $aValues = [];
669 - foreach ($aPositions as $sPosition) {
670 - $oRule = $aRules[sprintf($sExpanded, $sPosition)];
671 - $mRuleValue = $oRule->getValue();
672 - $aRuleValues = [];
673 - if (!$mRuleValue instanceof RuleValueList) {
674 - $aRuleValues[] = $mRuleValue;
675 - } else {
676 - $aRuleValues = $mRuleValue->getListComponents();
677 - }
678 - $aValues[$sPosition] = $aRuleValues;
679 - }
680 - $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
681 - if ((string)$aValues['left'][0] == (string)$aValues['right'][0]) {
682 - if ((string)$aValues['top'][0] == (string)$aValues['bottom'][0]) {
683 - if ((string)$aValues['top'][0] == (string)$aValues['left'][0]) {
684 - // All 4 sides are equal
685 - $oNewRule->addValue($aValues['top']);
686 - } else {
687 - // Top and bottom are equal, left and right are equal
688 - $oNewRule->addValue($aValues['top']);
689 - $oNewRule->addValue($aValues['left']);
690 - }
691 - } else {
692 - // Only left and right are equal
693 - $oNewRule->addValue($aValues['top']);
694 - $oNewRule->addValue($aValues['left']);
695 - $oNewRule->addValue($aValues['bottom']);
696 - }
697 - } else {
698 - // No sides are equal
699 - $oNewRule->addValue($aValues['top']);
700 - $oNewRule->addValue($aValues['left']);
701 - $oNewRule->addValue($aValues['bottom']);
702 - $oNewRule->addValue($aValues['right']);
703 - }
704 - $this->addRule($oNewRule);
705 - foreach ($aPositions as $sPosition) {
706 - $this->removeRule(sprintf($sExpanded, $sPosition));
707 - }
708 - }
709 - }
710 - }
711 -
712 - /**
713 - * Looks for long format CSS font properties (e.g. `font-weight`) and
714 - * tries to convert them into a shorthand CSS `font` property.
715 - *
716 - * At least `font-size` AND `font-family` must be present in order to create a shorthand declaration.
717 - *
718 - * @return void
719 - */
720 - public function createFontShorthand()
721 - {
722 - $aFontProperties = [
723 - 'font-style',
724 - 'font-variant',
725 - 'font-weight',
726 - 'font-size',
727 - 'line-height',
728 - 'font-family',
729 - ];
730 - $aRules = $this->getRulesAssoc();
731 - if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) {
732 - return;
733 - }
734 - $oOldRule = isset($aRules['font-size']) ? $aRules['font-size'] : $aRules['font-family'];
735 - $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo());
736 - unset($oOldRule);
737 - foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) {
738 - if (isset($aRules[$sProperty])) {
739 - $oRule = $aRules[$sProperty];
740 - $mRuleValue = $oRule->getValue();
741 - $aValues = [];
742 - if (!$mRuleValue instanceof RuleValueList) {
743 - $aValues[] = $mRuleValue;
744 - } else {
745 - $aValues = $mRuleValue->getListComponents();
746 - }
747 - if ($aValues[0] !== 'normal') {
748 - $oNewRule->addValue($aValues[0]);
749 - }
750 - }
751 - }
752 - // Get the font-size value
753 - $oRule = $aRules['font-size'];
754 - $mRuleValue = $oRule->getValue();
755 - $aFSValues = [];
756 - if (!$mRuleValue instanceof RuleValueList) {
757 - $aFSValues[] = $mRuleValue;
758 - } else {
759 - $aFSValues = $mRuleValue->getListComponents();
760 - }
761 - // But wait to know if we have line-height to add it
762 - if (isset($aRules['line-height'])) {
763 - $oRule = $aRules['line-height'];
764 - $mRuleValue = $oRule->getValue();
765 - $aLHValues = [];
766 - if (!$mRuleValue instanceof RuleValueList) {
767 - $aLHValues[] = $mRuleValue;
768 - } else {
769 - $aLHValues = $mRuleValue->getListComponents();
770 - }
771 - if ($aLHValues[0] !== 'normal') {
772 - $val = new RuleValueList('/', $this->iLineNo);
773 - $val->addListComponent($aFSValues[0]);
774 - $val->addListComponent($aLHValues[0]);
775 - $oNewRule->addValue($val);
776 - }
777 - } else {
778 - $oNewRule->addValue($aFSValues[0]);
779 - }
780 - $oRule = $aRules['font-family'];
781 - $mRuleValue = $oRule->getValue();
782 - $aFFValues = [];
783 - if (!$mRuleValue instanceof RuleValueList) {
784 - $aFFValues[] = $mRuleValue;
785 - } else {
786 - $aFFValues = $mRuleValue->getListComponents();
787 - }
788 - $oFFValue = new RuleValueList(',', $this->iLineNo);
789 - $oFFValue->setListComponents($aFFValues);
790 - $oNewRule->addValue($oFFValue);
791 -
792 - $this->addRule($oNewRule);
793 - foreach ($aFontProperties as $sProperty) {
794 - $this->removeRule($sProperty);
795 - }
796 - }
797 -
798 - /**
799 - * @return string
800 - *
801 - * @throws OutputException
802 - */
803 - public function __toString()
804 - {
805 - return $this->render(new OutputFormat());
806 - }
807 -
808 - /**
809 - * @return string
810 - *
811 - * @throws OutputException
812 - */
813 - public function render(OutputFormat $oOutputFormat)
814 - {
815 - if (count($this->aSelectors) === 0) {
816 - // If all the selectors have been removed, this declaration block becomes invalid
817 - throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo);
818 - }
819 - $sResult = $oOutputFormat->sBeforeDeclarationBlock;
820 - $sResult .= $oOutputFormat->implode(
821 - $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(),
822 - $this->aSelectors
823 - );
824 - $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors;
825 - $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{';
826 - $sResult .= parent::render($oOutputFormat);
827 - $sResult .= '}';
828 - $sResult .= $oOutputFormat->sAfterDeclarationBlock;
829 - return $sResult;
830 - }
831 -}