public function expandListStyleShorthand() { $aListProperties = array('list-style-type' => 'disc', 'list-style-position' => 'outside', 'list-style-image' => 'none'); $aListStyleTypes = array('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 = array('inside', 'outside'); $aRules = $this->getRules(); if (!isset($aRules['list-style'])) { return; } $oRule = $aRules['list-style']; $mRuleValue = $oRule->getValue(); $aValues = array(); if (!$mRuleValue instanceof CSSRuleValueList) { $aValues[] = $mRuleValue; } else { $aValues = $mRuleValue->getListComponents(); } if (count($aValues) == 1 && $aValues[0] == 'inherit') { foreach ($aListProperties as $sProperty => $mValue) { $oNewRule = new CSSRule($sProperty); $oNewRule->addValue('inherit'); $oNewRule->setIsImportant($oRule->getIsImportant()); $this->addRule($oNewRule); } $this->removeRule('list-style'); return; } foreach ($aValues as $mValue) { if (!$mValue instanceof CSSValue) { $mValue = mb_strtolower($mValue); } if ($mValue instanceof CSSUrl) { $aListProperties['list-style-image'] = $mValue; } else { if (in_array($mValue, $aListStyleTypes)) { $aListProperties['list-style-types'] = $mValue; } else { if (in_array($mValue, $aListStylePositions)) { $aListProperties['list-style-position'] = $mValue; } } } } foreach ($aListProperties as $sProperty => $mValue) { $oNewRule = new CSSRule($sProperty); $oNewRule->setIsImportant($oRule->getIsImportant()); $oNewRule->addValue($mValue); $this->addRule($oNewRule); } $this->removeRule('list-style'); }
private function parseRule() { $oRule = new CSSRule($this->parseIdentifier()); $this->consumeWhiteSpace(); $this->consume(':'); $oValue = $this->parseValue(self::listDelimiterForRule($oRule->getRule())); $oRule->setValue($oValue); if ($this->comes('!')) { $this->consume('!'); $this->consumeWhiteSpace(); $sImportantMarker = $this->consume(strlen('important')); if (mb_convert_case($sImportantMarker, MB_CASE_LOWER) !== 'important') { throw new Exception("! was followed by “" . $sImportantMarker . "”. Expected “important”"); } $oRule->setIsImportant(true); } if ($this->comes(';')) { $this->consume(';'); } return $oRule; }
/** * Merge multiple CSS RuleSets by cascading according to the CSS 2.1 cascading rules * (http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order). * * @param array $aDeclarations An array of CSSDeclarationBlock objects. * * @return CSSDeclarationBlock. * * ==== Cascading * If a CSSDeclarationBlock object has its +specificity+ defined, that specificity is * used in the cascade calculations. * * If no specificity is explicitly set and the CSSDeclarationBlock has *one* selector, * the specificity is calculated using that selector. * * If no selectors or multiple selectors are present, the specificity is * treated as 0. * * ==== Example #1 * <code> * $oDecl_1 = new CSSDeclarationBlock(); * $oRule_1 = new CSSRule('color'); * $oRule_1->addValue(new CSSColor('#F00')); * $oDecl_1->addRule($oRule_1); * $oDecl_2 = new CSSDeclarationBlock(); * $oRule_2 = new CSSRule('margin'); * $oRule_2->addValue( * new CSSSize(0, 'px') * ); * $oDecl_2->addRule($oRule_2); * * $oMerged = CSSDocument::mergeDeclarations($oDecl_1, $oDecl_2); * * echo $oMerged; * //=> "{ margin: 0px; color: rgb(255,0,0); }" * </code> * ==== Example #2 * <code> * $oDecl_1 = new CSSDeclarationBlock(); * $oRule_1 = new CSSRule('background-color'); * $oRule_1->addValue(new CSSColor('black')); * $oDecl_1->addRule($oRule_1); * $oDecl_2 = new CSSDeclarationBlock(); * $oRule_2 = new CSSRule('background-image'); * $oRule_2->addValue('none'); * $oDecl_2->addRule($oRule_2); * * $oMerged = CSSDocument::mergeDeclarations($oDecl_1, $oDecl_2); * * echo $oMerged; * //=> "{ background: none rgb(0,0,0); }" * </code> **/ static function mergeDeclarations(array $aDeclarations) { // Internal storage of CSS properties that we will keep $aProperties = array(); foreach ($aDeclarations as $oDeclaration) { $oDeclaration->expandShorthands(); $aSelectors = $oDeclaration->getSelectors(); $iSpecificity = 0; if (count($aSelectors) == 1) { $iSpecificity = $aSelectors[0]->getSpecificity(); } foreach ($oDeclaration->getRules() as $sProperty => $oRule) { // Add the property to the list to be folded per // http://www.w3.org/TR/CSS21/cascade.html#cascading-order if (!isset($aProperties[$sProperty])) { $aProperties[$sProperty] = array('values' => $oRule->getValues(), 'specificity' => $iSpecificity, 'important' => $oRule->getIsImportant()); } else { $aProperty = $aProperties[$sProperty]; if ($aProperty['specificity'] <= $iSpecificity && !$aProperty['important']) { $aProperties[$sProperty] = array('values' => $oRule->getValues(), 'specificity' => $iSpecificity, 'important' => $oRule->getIsImportant()); } } if ($oRule->getIsImportant()) { $aProperties[$sProperty] = array('values' => $oRule->getValues(), 'specificity' => $iSpecificity, 'important' => $oRule->getIsImportant()); } } // end foreach rules } // end foreach rulesets $oMerged = new CSSDeclarationBlock(); foreach ($aProperties as $sProperty => $aDetails) { $oNewRule = new CSSRule($sProperty); foreach ($aDetails['values'] as $aValue) { $oNewRule->addvalue($aValue); } if ($aDetails['important']) { $oNewRule->setIsImportant(true); } $oMerged->addRule($oNewRule); } $oMerged->createShorthands(); return $oMerged; }