You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
4.2 KiB
172 lines
4.2 KiB
2 years ago
|
<?php
|
||
|
|
||
|
namespace Sabberworm\CSS\CSSList;
|
||
|
|
||
|
use Sabberworm\CSS\Renderable;
|
||
|
use Sabberworm\CSS\RuleSet\DeclarationBlock;
|
||
|
use Sabberworm\CSS\RuleSet\RuleSet;
|
||
|
use Sabberworm\CSS\Property\Selector;
|
||
|
use Sabberworm\CSS\Comment\Commentable;
|
||
|
|
||
|
/**
|
||
|
* A CSSList is the most generic container available. Its contents include RuleSet as well as other CSSList objects.
|
||
|
* Also, it may contain Import and Charset objects stemming from @-rules.
|
||
|
*/
|
||
|
abstract class CSSList implements Renderable, Commentable {
|
||
|
|
||
|
protected $aComments;
|
||
|
protected $aContents;
|
||
|
protected $iLineNo;
|
||
|
|
||
|
public function __construct($iLineNo = 0) {
|
||
|
$this->aComments = array();
|
||
|
$this->aContents = array();
|
||
|
$this->iLineNo = $iLineNo;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return int
|
||
|
*/
|
||
|
public function getLineNo() {
|
||
|
return $this->iLineNo;
|
||
|
}
|
||
|
|
||
|
public function append($oItem) {
|
||
|
$this->aContents[] = $oItem;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Insert an item before its sibling.
|
||
|
*
|
||
|
* @param mixed $oItem The item.
|
||
|
* @param mixed $oSibling The sibling.
|
||
|
*/
|
||
|
public function insert($oItem, $oSibling) {
|
||
|
$iIndex = array_search($oSibling, $this->aContents);
|
||
|
if ($iIndex === false) {
|
||
|
return $this->append($oItem);
|
||
|
}
|
||
|
array_splice($this->aContents, $iIndex, 0, array($oItem));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes an item from the CSS list.
|
||
|
* @param RuleSet|Import|Charset|CSSList $oItemToRemove May be a RuleSet (most likely a DeclarationBlock), a Import, a Charset or another CSSList (most likely a MediaQuery)
|
||
|
*/
|
||
|
public function remove($oItemToRemove) {
|
||
|
$iKey = array_search($oItemToRemove, $this->aContents, true);
|
||
|
if ($iKey !== false) {
|
||
|
unset($this->aContents[$iKey]);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the contents.
|
||
|
* @param array $aContents Objects to set as content.
|
||
|
*/
|
||
|
public function setContents(array $aContents) {
|
||
|
$this->aContents = array();
|
||
|
foreach ($aContents as $content) {
|
||
|
$this->append($content);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes a declaration block from the CSS list if it matches all given selectors.
|
||
|
* @param array|string $mSelector The selectors to match.
|
||
|
* @param boolean $bRemoveAll Whether to stop at the first declaration block found or remove all blocks
|
||
|
*/
|
||
|
public function removeDeclarationBlockBySelector($mSelector, $bRemoveAll = false) {
|
||
|
if ($mSelector instanceof DeclarationBlock) {
|
||
|
$mSelector = $mSelector->getSelectors();
|
||
|
}
|
||
|
if (!is_array($mSelector)) {
|
||
|
$mSelector = explode(',', $mSelector);
|
||
|
}
|
||
|
foreach ($mSelector as $iKey => &$mSel) {
|
||
|
if (!($mSel instanceof Selector)) {
|
||
|
$mSel = new Selector($mSel);
|
||
|
}
|
||
|
}
|
||
|
foreach ($this->aContents as $iKey => $mItem) {
|
||
|
if (!($mItem instanceof DeclarationBlock)) {
|
||
|
continue;
|
||
|
}
|
||
|
if ($mItem->getSelectors() == $mSelector) {
|
||
|
unset($this->aContents[$iKey]);
|
||
|
if (!$bRemoveAll) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function __toString() {
|
||
|
return $this->render(new \Sabberworm\CSS\OutputFormat());
|
||
|
}
|
||
|
|
||
|
public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
|
||
|
$sResult = '';
|
||
|
$bIsFirst = true;
|
||
|
$oNextLevel = $oOutputFormat;
|
||
|
if(!$this->isRootList()) {
|
||
|
$oNextLevel = $oOutputFormat->nextLevel();
|
||
|
}
|
||
|
foreach ($this->aContents as $oContent) {
|
||
|
$sRendered = $oOutputFormat->safely(function() use ($oNextLevel, $oContent) {
|
||
|
return $oContent->render($oNextLevel);
|
||
|
});
|
||
|
if($sRendered === null) {
|
||
|
continue;
|
||
|
}
|
||
|
if($bIsFirst) {
|
||
|
$bIsFirst = false;
|
||
|
$sResult .= $oNextLevel->spaceBeforeBlocks();
|
||
|
} else {
|
||
|
$sResult .= $oNextLevel->spaceBetweenBlocks();
|
||
|
}
|
||
|
$sResult .= $sRendered;
|
||
|
}
|
||
|
|
||
|
if(!$bIsFirst) {
|
||
|
// Had some output
|
||
|
$sResult .= $oOutputFormat->spaceAfterBlocks();
|
||
|
}
|
||
|
|
||
|
return $sResult;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return true if the list can not be further outdented. Only important when rendering.
|
||
|
*/
|
||
|
public abstract function isRootList();
|
||
|
|
||
|
public function getContents() {
|
||
|
return $this->aContents;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $aComments Array of comments.
|
||
|
*/
|
||
|
public function addComments(array $aComments) {
|
||
|
$this->aComments = array_merge($this->aComments, $aComments);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getComments() {
|
||
|
return $this->aComments;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $aComments Array containing Comment objects.
|
||
|
*/
|
||
|
public function setComments(array $aComments) {
|
||
|
$this->aComments = $aComments;
|
||
|
}
|
||
|
|
||
|
}
|