From d1413c5f998378094f7c035210acd3427be602f1 Mon Sep 17 00:00:00 2001 From: Eric Ferraiuolo Date: Tue, 21 Jan 2014 13:32:55 -0500 Subject: [PATCH 1/8] Fix issue with parsing media query that does not have expressions Fixes #5 --- index.js | 6 ++++-- test/unit-tests.js | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 6cd4fad..b1d9b0d 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ exports.parse = parseQuery; // ----------------------------------------------------------------------------- -var RE_MEDIA_QUERY = /(?:(only|not)?\s*([^\s\(\)]+)\s*and\s*)?(.+)?/i, +var RE_MEDIA_QUERY = /(?:(only|not)?\s*([^\s\(\)]+)(?:\s*and)?\s*)?(.+)?/i, RE_MQ_EXPRESSION = /\(\s*([^\s\:\)]+)\s*(?:\:\s*([^\s\)]+))?\s*\)/, RE_MQ_FEATURE = /^(?:(min|max)-)?(.+)/, RE_LENGTH_UNIT = /(em|rem|px|cm|mm|in|pt|pc)?$/, @@ -86,10 +86,12 @@ function matchQuery(mediaQuery, values) { function parseQuery(mediaQuery) { return mediaQuery.split(',').map(function (query) { + query = query.trim(); + var captures = query.match(RE_MEDIA_QUERY), modifier = captures[1], type = captures[2], - expressions = captures[3], + expressions = captures[3] || '', parsed = {}; parsed.inverse = !!modifier && modifier.toLowerCase() === 'not'; diff --git a/test/unit-tests.js b/test/unit-tests.js index 95a81e9..3236695 100644 --- a/test/unit-tests.js +++ b/test/unit-tests.js @@ -5,6 +5,26 @@ var expect = require('chai').expect, mediaQuery = require('../'); +describe('mediaQuery.parse()', function () { + it('should parse media queries without expressions', function () { + expect(mediaQuery.parse('screen')).to.eql([ + { + inverse : false, + type : 'screen', + expressions: [] + } + ]); + + expect(mediaQuery.parse('not screen')).to.eql([ + { + inverse : true, + type : 'screen', + expressions: [] + } + ]); + }); +}); + describe('mediaQuery.match()', function () { describe('Equality Check', function () { it('Orientation: should return true for a correct match (===)', function () { @@ -279,7 +299,7 @@ describe('mediaQuery.match()', function () { }); }); - describe('#mediaQuery.match() Integration Tests', function () { + describe('mediaQuery.match() Integration Tests', function () { describe('Real World Use Cases (mostly AND)', function () { it('should return true because of width and type match', function () { expect(mediaQuery.match( From 315b8f2324f29b517a1a9dbb5dd2d2ba6c921f11 Mon Sep 17 00:00:00 2001 From: Eric Ferraiuolo Date: Tue, 21 Jan 2014 13:38:13 -0500 Subject: [PATCH 2/8] Bump version to v0.1.2 --- HISTORY.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 73ef943..d1915aa 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,15 @@ CSS Media Query Change History ============================== +0.1.2 (2014-01-21) +------------------ + +* Fixed issue with parsing media queries that do not have expressions. ([#5][]) + + +[#5]: https://github.com/ericf/css-mediaquery/issues/5 + + 0.1.1 (2014-01-08) ------------------ diff --git a/package.json b/package.json index 5bbc663..623f795 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-mediaquery", - "version": "0.1.1", + "version": "0.1.2", "description": "Parses and determines if a given CSS Media Query matches a set of values.", "main": "index.js", "scripts": { From afaa6cb37e7890732e22407f094d7a0aa68990b7 Mon Sep 17 00:00:00 2001 From: Eric Ferraiuolo Date: Fri, 24 Jan 2014 03:36:52 -0500 Subject: [PATCH 3/8] Improve parser to be more strict and throw for invalid media queries --- index.js | 43 +++++++++++++++++++++++++++++++++---------- test/unit-tests.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index b1d9b0d..2a7a936 100644 --- a/index.js +++ b/index.js @@ -11,11 +11,11 @@ exports.parse = parseQuery; // ----------------------------------------------------------------------------- -var RE_MEDIA_QUERY = /(?:(only|not)?\s*([^\s\(\)]+)(?:\s*and)?\s*)?(.+)?/i, - RE_MQ_EXPRESSION = /\(\s*([^\s\:\)]+)\s*(?:\:\s*([^\s\)]+))?\s*\)/, +var RE_MEDIA_QUERY = /^(?:(only|not)?\s*([_a-z][_a-z0-9-]*)|(\([^\)]+\)))(?:\s*and\s*(.*))?$/i, + RE_MQ_EXPRESSION = /^\(\s*([_a-z-][_a-z0-9-]*)\s*(?:\:\s*([^\)]+))?\s*\)$/, RE_MQ_FEATURE = /^(?:(min|max)-)?(.+)/, - RE_LENGTH_UNIT = /(em|rem|px|cm|mm|in|pt|pc)?$/, - RE_RESOLUTION_UNIT = /(dpi|dpcm|dppx)?$/; + RE_LENGTH_UNIT = /(em|rem|px|cm|mm|in|pt|pc)?\s*$/, + RE_RESOLUTION_UNIT = /(dpi|dpcm|dppx)?\s*$/; function matchQuery(mediaQuery, values) { return parseQuery(mediaQuery).some(function (query) { @@ -88,21 +88,44 @@ function parseQuery(mediaQuery) { return mediaQuery.split(',').map(function (query) { query = query.trim(); - var captures = query.match(RE_MEDIA_QUERY), - modifier = captures[1], + var captures = query.match(RE_MEDIA_QUERY); + + // Media Query must be valid. + if (!captures) { + throw new SyntaxError('Invalid CSS media query: ' + query); + } + + var modifier = captures[1], type = captures[2], - expressions = captures[3] || '', + expressions = ((captures[3] || '') + (captures[4] || '')).trim(), parsed = {}; parsed.inverse = !!modifier && modifier.toLowerCase() === 'not'; parsed.type = type ? type.toLowerCase() : 'all'; + // Check for media query expressions. + if (!expressions) { + parsed.expressions = []; + return parsed; + } + // Split expressions into a list. - expressions = expressions.match(/\([^\)]+\)/g) || []; + expressions = expressions.match(/\([^\)]+\)/g); + + // Media Query must be valid. + if (!expressions) { + throw new SyntaxError('Invalid CSS media query: ' + query); + } parsed.expressions = expressions.map(function (expression) { - var captures = expression.match(RE_MQ_EXPRESSION), - feature = captures[1].toLowerCase().match(RE_MQ_FEATURE); + var captures = expression.match(RE_MQ_EXPRESSION); + + // Media Query must be valid. + if (!captures) { + throw new SyntaxError('Invalid CSS media query: ' + query); + } + + var feature = captures[1].toLowerCase().match(RE_MQ_FEATURE); return { modifier: feature[1], diff --git a/test/unit-tests.js b/test/unit-tests.js index 3236695..2483df5 100644 --- a/test/unit-tests.js +++ b/test/unit-tests.js @@ -23,6 +23,37 @@ describe('mediaQuery.parse()', function () { } ]); }); + + it('should parse common retina media query list', function () { + var parsed = mediaQuery.parse( + 'only screen and (-webkit-min-device-pixel-ratio: 2),\n' + + 'only screen and ( min--moz-device-pixel-ratio: 2),\n' + + 'only screen and ( -o-min-device-pixel-ratio: 2/1),\n' + + 'only screen and ( min-device-pixel-ratio: 2),\n' + + 'only screen and ( min-resolution: 192dpi),\n' + + 'only screen and ( min-resolution: 2dppx)' + ); + + expect(parsed).to.be.an.array; + expect(parsed).to.have.length(6); + expect(parsed[0].expressions[0].feature).to.equal('-webkit-min-device-pixel-ratio'); + expect(parsed[1].expressions[0].modifier).to.equal('min'); + }); + + it('should throw a SyntaxError when a media query is invalid', function () { + function parse(query) { + return function () { mediaQuery.parse(query); }; + } + + expect(parse('some crap')).to.throw(SyntaxError); + expect(parse('48em')).to.throw(SyntaxError); + expect(parse('screen and crap')).to.throw(SyntaxError); + expect(parse('screen and (48em)')).to.throw(SyntaxError); + expect(parse('screen and (foo:)')).to.throw(SyntaxError); + expect(parse('()')).to.throw(SyntaxError); + expect(parse('(foo) (bar)')).to.throw(SyntaxError); + expect(parse('(foo:) and (bar)')).to.throw(SyntaxError); + }); }); describe('mediaQuery.match()', function () { From c8402506f8dd70cd9a3d5945bbf1c4683ec3cbff Mon Sep 17 00:00:00 2001 From: Eric Ferraiuolo Date: Fri, 24 Jan 2014 03:40:17 -0500 Subject: [PATCH 4/8] Improve error message --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 2a7a936..527b503 100644 --- a/index.js +++ b/index.js @@ -92,7 +92,7 @@ function parseQuery(mediaQuery) { // Media Query must be valid. if (!captures) { - throw new SyntaxError('Invalid CSS media query: ' + query); + throw new SyntaxError('Invalid CSS media query: +' + query + '"'); } var modifier = captures[1], @@ -114,7 +114,7 @@ function parseQuery(mediaQuery) { // Media Query must be valid. if (!expressions) { - throw new SyntaxError('Invalid CSS media query: ' + query); + throw new SyntaxError('Invalid CSS media query: +' + query + '"'); } parsed.expressions = expressions.map(function (expression) { @@ -122,7 +122,7 @@ function parseQuery(mediaQuery) { // Media Query must be valid. if (!captures) { - throw new SyntaxError('Invalid CSS media query: ' + query); + throw new SyntaxError('Invalid CSS media query: +' + query + '"'); } var feature = captures[1].toLowerCase().match(RE_MQ_FEATURE); From ad119660550806964fc91170cc7da83ac59c513b Mon Sep 17 00:00:00 2001 From: Eric Ferraiuolo Date: Fri, 24 Jan 2014 03:44:11 -0500 Subject: [PATCH 5/8] Actually improve error message --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 527b503..0783e84 100644 --- a/index.js +++ b/index.js @@ -92,7 +92,7 @@ function parseQuery(mediaQuery) { // Media Query must be valid. if (!captures) { - throw new SyntaxError('Invalid CSS media query: +' + query + '"'); + throw new SyntaxError('Invalid CSS media query: "' + query + '"'); } var modifier = captures[1], @@ -114,7 +114,7 @@ function parseQuery(mediaQuery) { // Media Query must be valid. if (!expressions) { - throw new SyntaxError('Invalid CSS media query: +' + query + '"'); + throw new SyntaxError('Invalid CSS media query: "' + query + '"'); } parsed.expressions = expressions.map(function (expression) { @@ -122,7 +122,7 @@ function parseQuery(mediaQuery) { // Media Query must be valid. if (!captures) { - throw new SyntaxError('Invalid CSS media query: +' + query + '"'); + throw new SyntaxError('Invalid CSS media query: "' + query + '"'); } var feature = captures[1].toLowerCase().match(RE_MQ_FEATURE); From 423abb50f46231a95ea8791b91702b35e0ad7c04 Mon Sep 17 00:00:00 2001 From: Eric Ferraiuolo Date: Fri, 24 Jan 2014 03:51:12 -0500 Subject: [PATCH 6/8] Add keywork to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 623f795..3a31c7e 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "media", "query", "mediaquery", + "media-query", "mobile", "parse", "match" From 34ba6343938e72b270205447b297611c40140a4f Mon Sep 17 00:00:00 2001 From: Eric Ferraiuolo Date: Fri, 24 Jan 2014 12:42:12 -0500 Subject: [PATCH 7/8] Add HISTORY entry to sticter parsing --- HISTORY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index d1915aa..b1268f0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,13 @@ CSS Media Query Change History ============================== +NEXT +---- + +* Made `parse()` sticter; it will now thorw a `SyntaxError` if a media query is + invalid. This makes it behave more like `JSON.parse()`. + + 0.1.2 (2014-01-21) ------------------ From c87f3c818162225d9f4d5d19a897156b3014663b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Brabec?= Date: Fri, 13 Sep 2024 03:57:53 +0200 Subject: [PATCH 8/8] Fixed package license identifier. (#23) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a31c7e..56a8970 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "contributors": [ "Tilo Mitra " ], - "license": "BSD", + "license": "BSD-3-Clause", "bugs": { "url": "https://github.com/ericf/css-mediaquery/issues" },