Esempio n. 1
0
 /**
  * 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);
 }
Esempio n. 2
0
 private function _createShorthandProperties(array $aProperties, $sShorthand, $bSafe = false)
 {
     if ($bSafe) {
         $bCanProceed = $this->_safeCleanup($aProperties, $sShorthand);
     } else {
         $bCanProceed = $this->_destructiveCleanup($aProperties, $sShorthand);
     }
     if (!$bCanProceed) {
         return;
     }
     // Now we collapse the rules
     $aNewValues = array('normal' => array(), 'important' => array());
     $aOldProperties = array('normal' => array(), 'important' => array());
     foreach ($aProperties as $sProperty) {
         $aProperties = $this->styleDeclaration->getProperties($sProperty);
         foreach ($aProperties as $iPos => $oProperty) {
             $aValues = $oProperty->getValueList()->getItems();
             $sDest = $oProperty->getIsImportant() ? 'important' : 'normal';
             $aOldProperties[$sDest][] = $iPos;
             foreach ($aValues as $mValue) {
                 $aNewValues[$sDest][] = Object::getClone($mValue);
             }
         }
     }
     $iImportantCount = count($aNewValues['important']);
     $iNormalCount = count($aNewValues['normal']);
     // Merge important values only if no normal values are present
     if ($iNormalCount) {
         $this->_mergeValues($sShorthand, $aNewValues['normal'], $aOldProperties['normal'], false);
     } else {
         if ($iImportantCount) {
             $this->_mergeValues($sShorthand, $aNewValues['important'], $aOldProperties['important'], true);
         }
     }
 }
Esempio n. 3
0
 private function _expandBackgroundShorthand($iPos, $oProperty)
 {
     /*{{{*/
     $oValueList = $oProperty->getValueList();
     // Get a normalized array
     if ($oValueList->getSeparator() === ',' && count($oValueList) > 1) {
         // we have multiple layers
         $aValueList = $oValueList->getItems();
     } else {
         // we have only one value or a space separated list of values
         $aValueList = array($oValueList->getItems());
     }
     $iNumLayers = count($aValueList);
     $aUnfoldedResults = array();
     // background-color only allowed on final layer;
     $color = null;
     foreach ($aValueList as $iLayerIndex => $aValues) {
         // if we have multiple layers, get the values for this layer
         if ($aValues instanceof PropertyValueList) {
             $aValues = $aValues->getItems();
         } else {
             if (!is_array($aValues)) {
                 $aValues = array($aValues);
             }
         }
         $aBgProperties = array();
         $iNumBgPos = 0;
         $iNumBoxValues = 0;
         foreach ($aValues as $mValue) {
             $mValue = Object::getClone($mValue);
             if ($mValue instanceof Value\Url || $mValue instanceof Value\Func || $mValue == "none") {
                 $aBgProperties['background-image'] = $mValue;
             } else {
                 if ($mValue instanceof PropertyValueList) {
                     // bg-pos bg-pos? / bg-size bg-size?
                     $oBgPosValues = $mValue->getFirst();
                     if ($oBgPosValues instanceof PropertyValueList) {
                         $aBgPosValues = $oBgPosValues->getItems();
                     } else {
                         $aBgPosValues = array($oBgPosValues);
                     }
                     $bgpos_valuelist = new PropertyValueList(array($aBgPosValues[0], 'center'), ' ');
                     if (count($aBgPosValues) > 1) {
                         $bgpos_valuelist->replace(1, $aBgPosValues[1]);
                     }
                     $aBgProperties['background-position'] = $bgpos_valuelist;
                     //
                     $oBgSizeValues = $mValue->getLast();
                     if ($oBgSizeValues instanceof PropertyValueList) {
                         $aBgSizeValues = $oBgSizeValues->getItems();
                     } else {
                         $aBgSizeValues = array($oBgSizeValues);
                     }
                     $bgsize_valuelist = new PropertyValueList(array($aBgSizeValues[0], $aBgSizeValues[0]), ' ');
                     if (count($aBgSizeValues) > 1) {
                         $bgsize_valuelist->replace(1, $aBgSizeValues[1]);
                     }
                     $aBgProperties['background-size'] = $bgsize_valuelist;
                 } else {
                     if (in_array($mValue, array('left', 'center', 'right', 'top', 'bottom')) || $mValue instanceof Value\Dimension) {
                         //if ($mValue instanceof Value\Dimension) $mValue = clone $mValue;
                         if ($iNumBgPos === 0) {
                             $aBgProperties['background-position'] = new PropertyValueList(array($mValue, 'center'), ' ');
                         } else {
                             $aBgProperties['background-position']->replace(1, $mValue);
                         }
                         $iNumBgPos++;
                     } else {
                         if (in_array($mValue, array('repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'space', 'round'))) {
                             $aBgProperties['background-repeat'] = $mValue;
                         } else {
                             if (in_array($mValue, array('scroll', 'fixed', 'local'))) {
                                 $aBgProperties['background-attachment'] = $mValue;
                             } else {
                                 if (in_array($mValue, array('border-box', 'padding-box', 'content-box'))) {
                                     if ($iNumBoxValues === 0) {
                                         $aBgProperties['background-origin'] = $mValue;
                                         $aBgProperties['background-clip'] = $mValue;
                                     } else {
                                         $aBgProperties['background-clip'] = $mValue;
                                     }
                                     $iNumBoxValues++;
                                 } else {
                                     if ($mValue instanceof Value\Color) {
                                         if ($iLayerIndex == $iNumLayers - 1) {
                                             $color = $mValue;
                                         } else {
                                             if (empty($aBgProperties)) {
                                                 $aBgProperties['background-image'] = "none";
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
         $aUnfoldedResults[] = $aBgProperties;
     }
     if ($color) {
         $aUnfoldedResults[$iNumLayers - 1]['background-color'] = $color;
     }
     $aFoldedResults = array();
     foreach ($aUnfoldedResults as $i => $result) {
         foreach ($result as $propname => $propval) {
             $aFoldedResults[$propname][$i] = $propval;
         }
     }
     foreach ($aFoldedResults as $propname => $aValues) {
         if ($this->_canAddShorthandExpansion($oProperty, $propname)) {
             $separator = count($aValues) === 0 ? ' ' : ',';
             $oValueList = new PropertyValueList($aValues, $separator);
             $oNewProp = new Property($propname, $oValueList);
             $oNewProp->setIsImportant($oProperty->getIsImportant());
             $this->styleDeclaration->insertAfter($oNewProp, $oProperty);
         }
     }
     $this->styleDeclaration->remove($oProperty);
 }