public function setStyleDeclaration(StyleDeclaration $style_declaration) { $style_declaration->setParentRule($this); if ($parentStyleSheet = $this->getParentStyleSheet()) { $style_declaration->setParentStyleSheet($parentStyleSheet); } $this->style_declaration = $style_declaration; }
/** * Merge multiple Css RuleSets by cascading according to the Css 3 cascading rules * (http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order). * * Cascading: * If a Css\StyleRule object has its +specificity+ defined, that specificity is * used in the cascade calculations. * * If no specificity is explicitly set and the Css\StyleRule has *one* selector, * the specificity is calculated using that selector. * * If no selectors or multiple selectors are present, the specificity is * treated as 0. * * * @param array $rules An array of Css\StyleRule objects * @return ju1ius\Css\StyleRule The merged ju1ius\Css\StyleRule * **/ public static function merge(array $rules) { if (1 === count($rules)) { if (!$rules[0] instanceof StyleRule) { throw new \InvalidArgumentException('You must provide an array of ju1ius\\Css\\StyleRule objects'); } return clone $rules[0]; } // Internal storage of Css properties that we will keep $aProperties = array(); foreach ($rules as $rule) { if (!$rule instanceof StyleRule) { throw new \InvalidArgumentException('You must provide an array of ju1ius\\Css\\StyleRule objects'); } $styleDeclaration = $rule->getStyleDeclaration(); $selectorList = $rule->getSelectorList(); $specificity = 0; // $styleDeclaration->expandShorthands(); if (1 === count($selectorList)) { $specificity = $selectorList[0]->getSpecificity(); } // foreach ($styleDeclaration->getAppliedProperties() as $name => $property) { // Add the property to the list to be folded per // http://www.w3.org/TR/css3-cascade/#cascading $override = false; $isImportant = $property->getIsImportant(); if (isset($aProperties[$name])) { $oldProp = $aProperties[$name]; // properties have same weight so we consider specificity if ($isImportant === $oldProp['property']->getIsImportant()) { if ($specificity >= $oldProp['specificity']) { $override = true; } } else { if ($isImportant) { $override = true; } } } else { $override = true; } if ($override) { $aProperties[$name] = array('property' => Object::getClone($property), 'specificity' => $specificity); } } } $merged = new StyleDeclaration(); foreach ($aProperties as $name => $details) { $merged->append($details['property']); } $merged->createShorthands(); return new StyleRule(new SelectorList(), $merged); }
/** * Not part of CSS grammar, but this pattern occurs frequently * in the official CSS grammar. Split out here to eliminate * duplicate code. * * @param boolean $check_start Indicates if the rule should check for the left brace at the beginning. * @param boolean $margins Indicates if the rule should check for @margin-box tokens. **/ protected function _parseDeclarations($check_start, $margins) { /*{{{*/ /** * Reads the pattern: * S* '{' S* declaration [ ';' S* declaration ]* '}' S* * or * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S* * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect. * A semicolon is only necessary following a declaration is there's another declaration * or margin afterwards. **/ $style_declaration = new StyleDeclaration(); if ($margins) { $margin_rules = new RuleList(); } $this->_ws(); if ($check_start) { $this->match(Lexer::T_LCURLY); } $this->_ws(); while (true) { try { $property = $this->_declaration(); } catch (ParseException $e) { if ($this->strict) { throw $e; } $this->skipDeclaration(); continue; } if (null !== $property) { $style_declaration->append($property); if ($this->LT()->type === Lexer::T_SEMICOLON) { $this->consume(); $this->_ws(); continue; } else { break; } } else { if ($margins && in_array($this->LT()->type, array(Lexer::T_TOPLEFTCORNER_SYM, Lexer::T_TOPLEFT_SYM, Lexer::T_TOPCENTER_SYM, Lexer::T_TOPRIGHT_SYM, Lexer::T_TOPRIGHTCORNER_SYM, Lexer::T_BOTTOMLEFTCORNER_SYM, Lexer::T_BOTTOMLEFT_SYM, Lexer::T_BOTTOMCENTER_SYM, Lexer::T_BOTTOMRIGHT_SYM, Lexer::T_BOTTOMRIGHTCORNER_SYM, Lexer::T_LEFTTOP_SYM, Lexer::T_LEFTMIDDLE_SYM, Lexer::T_LEFTBOTTOM_SYM, Lexer::T_RIGHTTOP_SYM, Lexer::T_RIGHTMIDDLE_SYM, Lexer::T_RIGHTBOTTOM_SYM))) { try { $margin_rules->append($this->_margin()); } catch (ParseException $e) { if ($this->strict) { throw $e; } $this->skipRuleset(true); } } else { break; } } } if ($check_start) { $this->match(Lexer::T_RCURLY); } $this->_ws(); return $margins ? array('style_declaration' => $style_declaration, 'rule_list' => $margin_rules) : $style_declaration; }