Skip to content

Commit 0b14b3b

Browse files
authored
Merge pull request MyIntervals#128 from raxbg/add_calc_support
Add support for CSS3 calc
2 parents c3b01ef + d7091aa commit 0b14b3b

File tree

8 files changed

+85
-3
lines changed

8 files changed

+85
-3
lines changed

lib/Sabberworm/CSS/OutputFormat.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,4 +286,4 @@ private function prepareSpace($sSpaceString) {
286286
private function indent() {
287287
return str_repeat($this->oFormat->sIndentation, $this->oFormat->level());
288288
}
289-
}
289+
}

lib/Sabberworm/CSS/Parser.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
use Sabberworm\CSS\CSSList\AtRuleBlockList;
1515
use Sabberworm\CSS\RuleSet\DeclarationBlock;
1616
use Sabberworm\CSS\Value\CSSFunction;
17+
use Sabberworm\CSS\Value\CalcFunction;
1718
use Sabberworm\CSS\Value\RuleValueList;
19+
use Sabberworm\CSS\Value\CalcRuleValueList;
1820
use Sabberworm\CSS\Value\Size;
1921
use Sabberworm\CSS\Value\Color;
2022
use Sabberworm\CSS\Value\URL;
@@ -434,6 +436,8 @@ private function parsePrimitiveValue() {
434436
$oValue = $this->parseColorValue();
435437
} else if ($this->comes('url', true)) {
436438
$oValue = $this->parseURLValue();
439+
} else if ($this->comes('calc', true) || $this->comes('-webkit-calc', true) || $this->comes('-moz-calc', true)) {
440+
$oValue = $this->parseCalcValue();
437441
} else if ($this->comes("'") || $this->comes('"')) {
438442
$oValue = $this->parseStringValue();
439443
} else if ($this->comes("progid:") && $this->oParserSettings->bLenientParsing) {
@@ -520,6 +524,42 @@ private function parseURLValue() {
520524
return $oResult;
521525
}
522526

527+
private function parseCalcValue() {
528+
$aOperators = array('+', '-', '*', '/', '(', ')');
529+
$sFunction = trim($this->consumeUntil('(', false, true));
530+
$oCalcList = new CalcRuleValueList($this->iLineNo);
531+
$oList = new RuleValueList(',', $this->iLineNo);
532+
$iNestingLevel = 0;
533+
$iLastComponentType = NULL;
534+
while(!$this->comes(')') || $iNestingLevel > 0) {
535+
$this->consumeWhiteSpace();
536+
if (in_array($this->peek(), $aOperators)) {
537+
if (($this->comes('-') || $this->comes('+'))) {
538+
if ($this->peek(1, -1) != ' ' || !($this->comes('- ') || $this->comes('+ '))) {
539+
throw new UnexpectedTokenException(" {$this->peek()} ", $this->peek(1, -1) . $this->peek(2), 'literal', $this->iLineNo);
540+
}
541+
} else if ($this->comes('(')) {
542+
$iNestingLevel++;
543+
} else if ($this->comes(')')) {
544+
$iNestingLevel--;
545+
}
546+
$oCalcList->addListComponent($this->consume(1));
547+
$iLastComponentType = CalcFunction::T_OPERATOR;
548+
} else {
549+
$oVal = $this->parsePrimitiveValue();
550+
if ($iLastComponentType == CalcFunction::T_OPERAND) {
551+
throw new UnexpectedTokenException(sprintf('Next token was expected to be an operand of type %s. Instead "%s" was found.', implode(', ', $aOperators), $oVal), '', 'custom', $this->iLineNo);
552+
}
553+
554+
$oCalcList->addListComponent($oVal);
555+
$iLastComponentType = CalcFunction::T_OPERAND;
556+
}
557+
}
558+
$oList->addListComponent($oCalcList);
559+
$this->consume(')');
560+
return new CalcFunction($sFunction, $oList, ',', $this->iLineNo);
561+
}
562+
523563
/**
524564
* Tests an identifier for a given value. Since identifiers are all keywords, they can be vendor-prefixed. We need to check for these versions too.
525565
*/

lib/Sabberworm/CSS/Value/CSSFunction.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
class CSSFunction extends ValueList {
66

7-
private $sName;
7+
protected $sName;
88

99
public function __construct($sName, $aArguments, $sSeparator = ',', $iLineNo = 0) {
1010
if($aArguments instanceof RuleValueList) {
@@ -37,4 +37,4 @@ public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
3737
return "{$this->sName}({$aArguments})";
3838
}
3939

40-
}
40+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Value;
4+
5+
class CalcFunction extends CSSFunction {
6+
const T_OPERAND = 1;
7+
const T_OPERATOR = 2;
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Value;
4+
5+
class CalcRuleValueList extends RuleValueList {
6+
public function __construct($iLineNo = 0) {
7+
parent::__construct(array(), ',', $iLineNo);
8+
}
9+
10+
public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
11+
return $oOutputFormat->implode(' ', $this->aComponents);
12+
}
13+
14+
}

tests/Sabberworm/CSS/ParserTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,20 @@ function testUrlInFile() {
387387
$this->assertSame($sExpected, $oDoc->render());
388388
}
389389

390+
function testCalcInFile() {
391+
$oDoc = $this->parsedStructureForFile('calc', Settings::create()->withMultibyteSupport(true));
392+
$sExpected = 'div {width: calc(100% / 4);}
393+
div {height: -webkit-calc(9 / 16 * 100%) !important;width: -moz-calc(( 50px - 50% ) * 2);}';
394+
$this->assertSame($sExpected, $oDoc->render());
395+
}
396+
397+
/**
398+
* @expectedException Sabberworm\CSS\Parsing\UnexpectedTokenException
399+
*/
400+
function testCalcFailure() {
401+
$this->parsedStructureForFile('-calc-no-space-around-minus', Settings::create()->withLenientParsing(false));
402+
}
403+
390404
function testUrlInFileMbOff() {
391405
$oDoc = $this->parsedStructureForFile('url', Settings::create()->withMultibyteSupport(false));
392406
$sExpected = 'body {background: #fff url("http://somesite.com/images/someimage.gif") repeat top center;}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
div { width: calc(50% -8px); }

tests/files/calc.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
div { width: calc(100% / 4); }
2+
div {
3+
height: -webkit-calc(9/16 * 100%)!important;
4+
width: -moz-calc((50px - 50%)*2);
5+
}

0 commit comments

Comments
 (0)