diff --git a/index.js b/index.js index d3af489..1545ad4 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,8 @@ module.exports = parse; function parse(str) { - return new Parser(str).parse(); + var cleanStr = str.replace(/^\s+|\s+$/, ''); + return new Parser(cleanStr).parse(); } function Parser(str) { @@ -10,20 +11,27 @@ function Parser(str) { } Parser.prototype.skip = function(m){ - this.str = this.str.slice(m[0].length); + this.str = this.str.slice(m.length); }; Parser.prototype.comma = function(){ var m = /^, */.exec(this.str); if (!m) return; - this.skip(m); + this.skip(m[0]); return { type: 'comma', string: ',' }; }; +Parser.prototype.operator = function(){ + var m = /^\/ */.exec(this.str); + if (!m) return; + this.skip(m[0]); + return { type: 'operator', value: '/' }; +}; + Parser.prototype.ident = function(){ var m = /^([\w-]+) */.exec(this.str); if (!m) return; - this.skip(m); + this.skip(m[0]); return { type: 'ident', string: m[1] @@ -31,9 +39,9 @@ Parser.prototype.ident = function(){ }; Parser.prototype.int = function(){ - var m = /^((\d+)(\S+)?) */.exec(this.str); + var m = /^((-?\d+)([^\s\/]+)?) */.exec(this.str); if (!m) return; - this.skip(m); + this.skip(m[0]); var n = ~~m[2]; var u = m[3]; @@ -46,9 +54,9 @@ Parser.prototype.int = function(){ }; Parser.prototype.float = function(){ - var m = /^(((?:\d+)?\.\d+)(\S+)?) */.exec(this.str); + var m = /^((-?(?:\d+)?\.\d+)([^\s\/]+)?) */.exec(this.str); if (!m) return; - this.skip(m); + this.skip(m[0]); var n = parseFloat(m[2]); var u = m[3]; @@ -67,7 +75,7 @@ Parser.prototype.number = function(){ Parser.prototype.double = function(){ var m = /^"([^"]*)" */.exec(this.str); if (!m) return m; - this.skip(m); + this.skip(m[0]); return { type: 'string', quote: '"', @@ -79,7 +87,7 @@ Parser.prototype.double = function(){ Parser.prototype.single = function(){ var m = /^'([^']*)' */.exec(this.str); if (!m) return m; - this.skip(m); + this.skip(m[0]); return { type: 'string', quote: "'", @@ -92,9 +100,72 @@ Parser.prototype.string = function(){ return this.single() || this.double(); }; +Parser.prototype.color = function(){ + var m = /^(rgba?\([^)]*\)) */.exec(this.str); + if (!m) return m; + this.skip(m[0]); + return { + type: 'color', + value: m[1] + } +}; + +Parser.prototype.url = function(){ + var m = /^(url\([^)]*\)) */.exec(this.str); + if (!m) return m; + this.skip(m[0]); + return { + type: 'url', + value: m[1] + } +}; + +function readToMatchingParen(str) { + if(str[0] !== '(') { + throw new Error('expected opening paren'); + } + + var opens = 0; + for(var i = 0; i < str.length; i++) { + if(str[i] === '(') { + opens++; + } + else if(str[i] === ')') { + opens--; + } + + if(opens === 0) { + break; + } + } + + if(opens !== 0) { + throw new Error('Failed parsing: No matching paren'); + } + + return str.slice(0, i + 1); +} + +Parser.prototype.gradient = function(){ + var m = /^(radial|linear)-gradient/.exec(this.str); + if (!m) return m; + this.skip(m[0]); + + var gradientStr = readToMatchingParen(this.str); + this.skip(gradientStr); + return { + type: 'gradient', + value: m[0] + gradientStr + } +}; Parser.prototype.value = function(){ - return this.number() + this.str = this.str.replace(/^\s+/, ''); + return this.operator() + || this.number() + || this.color() + || this.gradient() + || this.url() || this.ident() || this.string() || this.comma(); diff --git a/package.json b/package.json index 086525a..7dc6607 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,12 @@ { "name": "css-value", - "version": "0.0.1", + "version": "0.0.2", "description": "CSS value parser", - "keywords": ["css", "parser", "value"], + "keywords": [ + "css", + "parser", + "value" + ], "author": "TJ Holowaychuk ", "repository": { "type": "git", @@ -13,5 +17,8 @@ "mocha": "~1.9.0", "should": "~1.2.2" }, - "main": "index" + "main": "index", + "scripts": { + "test": "mocha -r should" + } } diff --git a/test/cases/colors.js b/test/cases/colors.js new file mode 100644 index 0000000..28f690d --- /dev/null +++ b/test/cases/colors.js @@ -0,0 +1,6 @@ +exports.string = 'rgba(1,2,3,4) rgb(9,8,7)'; + +exports.object = [ + { type: 'color', value: 'rgba(1,2,3,4)' }, + { type: 'color', value: 'rgb(9,8,7)' } +]; diff --git a/test/cases/gradients.js b/test/cases/gradients.js new file mode 100644 index 0000000..3d8da89 --- /dev/null +++ b/test/cases/gradients.js @@ -0,0 +1,6 @@ +exports.string = 'linear-gradient(to bottom, rgb(142, 75, 75), rgb(0, 0, 0))'; + +exports.object = [ + { type: 'gradient', value: 'linear-gradient(to bottom, rgb(142, 75, 75), rgb(0, 0, 0))' } +]; + diff --git a/test/cases/numbers.js b/test/cases/numbers.js index 353aac9..2408d00 100644 --- a/test/cases/numbers.js +++ b/test/cases/numbers.js @@ -1,5 +1,5 @@ -exports.string = '1px 0 0 5% .5px .10 1.5'; +exports.string = '1px 0 0 5% .5px .10 1.5 -1 -0.1'; exports.object = [ { type: 'number', string: '1px', unit: 'px', value: 1 }, @@ -8,5 +8,7 @@ exports.object = [ { type: 'number', string: '5%', unit: '%', value: 5 }, { type: 'number', string: '.5px', unit: 'px', value: .5 }, { type: 'number', string: '.10', unit: '', value: .1 }, - { type: 'number', string: '1.5', unit: '', value: 1.5 } + { type: 'number', string: '1.5', unit: '', value: 1.5 }, + { type: 'number', string: '-1', unit: '', value: -1 }, + { type: 'number', string: '-0.1', unit: '', value: -0.1 } ]; diff --git a/test/cases/operators.js b/test/cases/operators.js new file mode 100644 index 0000000..cc4d113 --- /dev/null +++ b/test/cases/operators.js @@ -0,0 +1,12 @@ +exports.string = '50px/50% auto/auto'; + +exports.object = [ + { type: 'number', string: '50px', unit: 'px', value: 50 }, + { type: 'operator', value: '/' }, + { type: 'number', string: '50%', unit: '%', value: 50 }, + { type: 'ident', string: 'auto' }, + { type: 'operator', value: '/' }, + { type: 'ident', string: 'auto' } +]; + + diff --git a/test/cases/space-in-front.js b/test/cases/space-in-front.js new file mode 100644 index 0000000..7ba1057 --- /dev/null +++ b/test/cases/space-in-front.js @@ -0,0 +1,5 @@ +exports.string = ' rgba(1,2,3,4)'; + +exports.object = [ + { type: 'color', value: 'rgba(1,2,3,4)' } +] diff --git a/test/cases/urls.js b/test/cases/urls.js new file mode 100644 index 0000000..d2403e1 --- /dev/null +++ b/test/cases/urls.js @@ -0,0 +1,6 @@ +exports.string = 'url(http://foo.bar.com/whatever.png)'; + +exports.object = [ + { type: 'url', value: 'url(http://foo.bar.com/whatever.png)' } +]; + diff --git a/test/index.js b/test/index.js index 77b6776..e38ca14 100644 --- a/test/index.js +++ b/test/index.js @@ -6,7 +6,9 @@ var readdir = fs.readdirSync; var path = require('path'); var basename = path.basename; -readdir('test/cases').forEach(function(file){ +readdir('test/cases') +.filter(function(fn) { return /\.js$/.test(fn); }) +.forEach(function(file){ var mod = require(path.resolve('test/cases/' + file)); var title = basename(file, '.js'); it('should support ' + title, function(){