diff --git a/lib/parser.js b/lib/parser.js index 2c22930..32e37e7 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -79,12 +79,12 @@ module.exports = (function() { matchGradient( 'radial-gradient', tokens.radialGradient, - matchRadialOrientation) || + matchListRadialOrientations) || matchGradient( 'repeating-radial-gradient', tokens.repeatingRadialGradient, - matchRadialOrientation); + matchListRadialOrientations); } function matchGradient(gradientType, pattern, orientationMatcher) { @@ -128,9 +128,6 @@ module.exports = (function() { matchAngle(); } - function matchRadialOrientation() { - } - function matchSideOrCorner() { return match('directional', tokens.sideOrCorner, 1); } @@ -139,6 +136,102 @@ module.exports = (function() { return match('angular', tokens.angleValue, 1); } + function matchListRadialOrientations() { + var radialOrientations, + radialOrientation = matchRadialOrientation(), + lookaheadCache; + + if (radialOrientation) { + radialOrientations = []; + radialOrientations.push(radialOrientation); + + lookaheadCache = input; + if (scan(tokens.comma)) { + radialOrientation = matchRadialOrientation(); + if (radialOrientation) { + radialOrientations.push(radialOrientation); + } else { + input = lookaheadCache; + } + } + } + + return radialOrientations; + } + + function matchRadialOrientation() { + var radialType = matchCircle() || + matchEllipse(); + + if (radialType) { + radialType.at = matchAtPosition(); + } else { + var defaultPosition = matchPositioning(); + if (defaultPosition) { + radialType = { + type: 'default', + at: defaultPosition + }; + } + } + + return radialType; + } + + function matchCircle() { + var circle = match('shape', /^(circle)/i, 0); + + if (circle) { + circle.style = matchLength() || matchExtentKeyword(); + } + + return circle; + } + + function matchEllipse() { + var ellipse = match('shape', /^(ellipse)/i, 0); + + if (ellipse) { + ellipse.style = matchExtentKeyword() || matchDistance(); + } + + return ellipse; + } + + function matchExtentKeyword() { + return match('extent-keyword', /^(closest\-side|closest\-corner|farthest\-side|farthest\-corner|contain|cover)/, 1); + } + + function matchAtPosition() { + if (match('position', /^at/, 0)) { + var positioning = matchPositioning(); + + if (!positioning) { + error('Missing positioning value'); + } + + return positioning; + } + } + + function matchPositioning() { + var location = matchCoordinates(); + + if (location.x || location.y) { + return { + type: 'position', + value: location + }; + } + } + + function matchCoordinates() { + return { + x: matchDistance(), + y: matchDistance() + }; + } + function matchListing(matcher) { var captures = matcher(), result = []; @@ -165,7 +258,7 @@ module.exports = (function() { error('Expected color definition'); } - color.length = matchLength(); + color.length = matchDistance(); return color; } @@ -206,9 +299,18 @@ module.exports = (function() { return scan(tokens.number)[1]; } + function matchDistance() { + return match('%', tokens.percentageValue, 1) || + matchPositionKeyword() || + matchLength(); + } + + function matchPositionKeyword() { + return match('positionKeyword', /^(left|center|right|top|bottom)/i, 1); + } + function matchLength() { return match('px', tokens.pixelValue, 1) || - match('%', tokens.percentageValue, 1) || match('em', tokens.emValue, 1); } diff --git a/spec/parser.spec.js b/spec/parser.spec.js index cb389d8..add899a 100644 --- a/spec/parser.spec.js +++ b/spec/parser.spec.js @@ -218,4 +218,20 @@ describe('gradient-parser.js', function () { }); }); + describe('different radial declarations', function() { + [ + 'ellipse farthest-corner', + 'ellipse cover', + 'circle cover', + 'center bottom, ellipse cover', + 'circle at 119px 58px' + ].forEach(function(declaration) { + + it('should parse ' + declaration + ' declaration', function() { + ast = gradients.parse('radial-gradient(' + declaration + ', red, blue)'); + }); + + }); + }); + });