From 6204d9f417e6d669421968ad20b89edba24e3104 Mon Sep 17 00:00:00 2001 From: udivankin Date: Thu, 16 Aug 2018 19:04:10 +0300 Subject: [PATCH 1/4] Adds loose comparison support --- README.md | 7 +++++++ src/parser.js | 7 ++++++- src/utils.js | 30 +++++++++++++++++++----------- test/unit/parser.js | 7 ++++++- test/unit/utils.js | 8 ++++++-- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ee53c41..dd04390 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,13 @@ const isValidBrowser = browser.satisfies({ chrome: ">20.1.1432", firefox: ">31", opera: ">22" + + // also supports equality operator + chrome: "=20.1.1432", // will match particular build only + + // and loose-equality operator + chrome: "~20" // will match any 20.* sub-version + chrome: "~20.1" // will match any 20.1.* sub-version (20.1.19 as well as 20.1.12.42-alpha.1) }); ``` diff --git a/src/parser.js b/src/parser.js index df98540..283f7df 100644 --- a/src/parser.js +++ b/src/parser.js @@ -397,6 +397,7 @@ class Parser { compareVersion(version) { let expectedResult = 0; let comparableVersion = version; + let isLoose = false; if (version[0] === '>') { expectedResult = 1; @@ -406,8 +407,12 @@ class Parser { comparableVersion = version.substr(1); } else if (version[0] === '=') { comparableVersion = version.substr(1); + } else if (version[0] === '~') { + isLoose = true; + comparableVersion = version.substr(1); } - return compareVersions(this.getBrowserVersion(), comparableVersion) === expectedResult; + + return compareVersions(this.getBrowserVersion(), comparableVersion, isLoose) === expectedResult; } isOS(osName) { diff --git a/src/utils.js b/src/utils.js index b40a849..4d313e6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -68,23 +68,26 @@ class Utils { * Calculate browser version weight * * @example - * compareVersions(['1.10.2.1', '1.8.2.1.90']) // 1 - * compareVersions(['1.010.2.1', '1.09.2.1.90']); // 1 - * compareVersions(['1.10.2.1', '1.10.2.1']); // 0 - * compareVersions(['1.10.2.1', '1.0800.2']); // -1 + * compareVersions('1.10.2.1', '1.8.2.1.90') // 1 + * compareVersions('1.010.2.1', '1.09.2.1.90'); // 1 + * compareVersions('1.10.2.1', '1.10.2.1'); // 0 + * compareVersions('1.10.2.1', '1.0800.2'); // -1 + * compareVersions('1.10.2.1', '1.10', true); // 0 * * @param {String} versionA versions versions to compare * @param {String} versionB versions versions to compare + * @param {boolean} [isLoose] enable loose comparison * @return {Number} comparison result: -1 when versionA is lower, * 1 when versionA is bigger, 0 when both equal */ /* eslint consistent-return: 1 */ - static compareVersions(versionA, versionB) { + static compareVersions(versionA, versionB, isLoose = false) { // 1) get common precision for both versions, for example for "10.0" and "9" it should be 2 - let precision = Math.max( - Utils.getVersionPrecision(versionA), - Utils.getVersionPrecision(versionB), - ); + const versionAPrecision = Utils.getVersionPrecision(versionA); + const versionBPrecision = Utils.getVersionPrecision(versionB); + + let precision = Math.max(versionAPrecision, versionBPrecision); + let lastPrecision = 0; const chunks = Utils.map([versionA, versionB], (version) => { const delta = precision - Utils.getVersionPrecision(version); @@ -96,14 +99,19 @@ class Utils { return Utils.map(_version.split('.'), chunk => new Array(20 - chunk.length).join('0') + chunk).reverse(); }); + // adjust precision for loose comparison + if (isLoose) { + lastPrecision = precision - Math.min(versionAPrecision, versionBPrecision); + } + // iterate in reverse order by reversed chunks array precision -= 1; - while (precision >= 0) { + while (precision >= lastPrecision) { // 4) compare: "000000009" > "000000010" = false (but "9" > "10" = true) if (chunks[0][precision] > chunks[1][precision]) { return 1; } else if (chunks[0][precision] === chunks[1][precision]) { - if (precision === 0) { + if (precision === lastPrecision) { // all version chunks are same return 0; } diff --git a/test/unit/parser.js b/test/unit/parser.js index afb643e..5e37a0b 100644 --- a/test/unit/parser.js +++ b/test/unit/parser.js @@ -57,8 +57,13 @@ test('Skip parsing shouldn\'t parse', (t) => { t.deepEqual((new Parser(UA, true)).getResult(), {}); }); -test('Parser.check should make simple comparison', (t) => { +test('Parser.check should make simple comparisons', (t) => { + // also covers Parser.compareVersion() method t.is(parser.satisfies({ opera: '>42' }), true); + t.is(parser.satisfies({ opera: '<44' }), true); + t.is(parser.satisfies({ opera: '=43.0.2442.1165' }), true); + t.is(parser.satisfies({ opera: '~43.0' }), true); + t.is(parser.satisfies({ opera: '~43' }), true); }); test('Parser.check should make complex comparison', (t) => { diff --git a/test/unit/utils.js b/test/unit/utils.js index 0240976..4d73cbc 100644 --- a/test/unit/utils.js +++ b/test/unit/utils.js @@ -22,6 +22,9 @@ test('compareVersions', (t) => { ['1.10.2.1', '1.8.2.1.90', 1], ['1.010.2.1', '1.08.2.1.90', 1], ['1.10.2.1', '1.10.2.1', 0], + ['1.10.2.1', '1.10.2', 0, true], + ['1.10.2.1', '1.10', 0, true], + ['1.10.2.1', '1', 0, true], ['1.10.2.1', '1.0800.2', -1], ['1.0.0-alpha', '1.0.0-alpha.1', -1], ['1.0.0-alpha.1', '1.0.0-alpha.beta', -1], @@ -35,7 +38,8 @@ test('compareVersions', (t) => { const versionA = testingParams[0]; const versionB = testingParams[1]; const result = testingParams[2]; - let matching = ' == '; + const isLoose = testingParams.length > 3 ? testingParams[3] : false; + let matching = isLoose ? '~' : ' == '; if (result > 0) { matching = ' > '; @@ -43,6 +47,6 @@ test('compareVersions', (t) => { matching = ' < '; } - t.is(compareVersions(versionA, versionB), result, `version ${versionA} should be ${matching} version ${versionB}`); + t.is(compareVersions(versionA, versionB, isLoose), result, `version ${versionA} should be ${matching} version ${versionB}`); }); }); From d039ce020e65755583e6ad33f7254cd4a43fb2e1 Mon Sep 17 00:00:00 2001 From: Denis Demchenko Date: Sat, 18 Aug 2018 14:13:50 +0300 Subject: [PATCH 2/4] Add contributing.md --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8b19427 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Contributing + +The project runs Git-flow, where the `master` branch is the production one and the `develop` is the developing one. + +In a nutshell, if you're about to propose a new feature with adding new functionality to bowser, it's better to branch from `develop` and make a PR pointing to `develop` as well. +If it's a small hotfix, fix a typo in the docs or you've added support for a new browser/OS/platform/etc, then it's better to branch from `master` and make a PR pointing to `master` as well. +Following these simple rules will help to maintain the repo a lot! Thanks ❤️ From 2bc8982b428de4d0856a6756c6e1521810a917c3 Mon Sep 17 00:00:00 2001 From: Denis Demchenko Date: Sat, 18 Aug 2018 14:14:56 +0300 Subject: [PATCH 3/4] Regen docs --- docs/Bowser.html | 2 +- docs/Parser.html | 6 +++--- docs/bowser.js.html | 4 ++-- docs/global.html | 2 +- docs/index.html | 16 +++++++++++----- docs/parser.js.html | 9 +++++++-- docs/utils.js.html | 32 ++++++++++++++++++++------------ 7 files changed, 45 insertions(+), 26 deletions(-) diff --git a/docs/Bowser.html b/docs/Bowser.html index dffd5cf..fc09b9d 100644 --- a/docs/Bowser.html +++ b/docs/Bowser.html @@ -559,7 +559,7 @@ bowser.getResult()
- Documentation generated by JSDoc 3.5.5 on Sun Jul 22 2018 19:42:18 GMT+0300 (EEST) using the docdash theme. + Documentation generated by JSDoc 3.5.5 on Sat Aug 18 2018 14:14:47 GMT+0300 (EEST) using the docdash theme.
diff --git a/docs/Parser.html b/docs/Parser.html index 40bf929..d56ee5c 100644 --- a/docs/Parser.html +++ b/docs/Parser.html @@ -1558,7 +1558,7 @@ like Parser#parseBrowser or Source:
@@ -2376,7 +2376,7 @@ Returns undefined when the browser is no described in the checkTree
Source:
@@ -2679,7 +2679,7 @@ Returns undefined when the browser is no described in the checkTree
- Documentation generated by JSDoc 3.5.5 on Sun Jul 22 2018 19:42:18 GMT+0300 (EEST) using the docdash theme. + Documentation generated by JSDoc 3.5.5 on Sat Aug 18 2018 14:14:47 GMT+0300 (EEST) using the docdash theme.
diff --git a/docs/bowser.js.html b/docs/bowser.js.html index aad1c8f..4de7f93 100644 --- a/docs/bowser.js.html +++ b/docs/bowser.js.html @@ -65,7 +65,7 @@ class Bowser { * const bowser = new Bowser(window.navigator.userAgent); * bowser.getResult() */ - static getParser(UA, skipParsing=false) { + static getParser(UA, skipParsing = false) { if (typeof UA !== 'string') { throw new Error('UserAgent should be a string'); } @@ -96,7 +96,7 @@ export default Bowser;
- Documentation generated by JSDoc 3.5.5 on Sun Jul 22 2018 19:42:18 GMT+0300 (EEST) using the docdash theme. + Documentation generated by JSDoc 3.5.5 on Sat Aug 18 2018 14:14:47 GMT+0300 (EEST) using the docdash theme.
diff --git a/docs/global.html b/docs/global.html index 5e2a961..e84f500 100644 --- a/docs/global.html +++ b/docs/global.html @@ -758,7 +758,7 @@ like "iPhone" or "Kindle Fire HD 7"
- Documentation generated by JSDoc 3.5.5 on Sun Jul 22 2018 19:42:18 GMT+0300 (EEST) using the docdash theme. + Documentation generated by JSDoc 3.5.5 on Sat Aug 18 2018 14:14:47 GMT+0300 (EEST) using the docdash theme.
diff --git a/docs/index.html b/docs/index.html index efeb771..d192c10 100644 --- a/docs/index.html +++ b/docs/index.html @@ -59,13 +59,12 @@

Changes of the 2.0 The upcoming 2.0 version has drastically changed API. All available methods can be found in the docs folder from now on and on a webpage soon.

Use cases

First of all, require the library:

-
const bowser = require('bowser');

By default, require('bowser') requires the ES6 version of files, which +

const bowser = require('bowser');

By default, require('bowser') requires the ES5 version of files, which do not include any polyfills.

In case if you don't use your own babel-polyfill you may need to have pre-built bundle with all needed polyfills. So, for you it's suitable to require bowser like this: require('bowser/bundled'). As the result, you get a ES5 version of bowser with babel-polyfill bundled together.

-

If you use bowser for Node.js, you'd better use require('bowser/es5'), -since source files have import statements, which are not compatible with Node.js yet.

+

You may need to use the source files, so they will be available in the package as well.

Browser props detection

Often we need to pick users' browser properties such as the name, the version, the rendering engine and so on. Here is an example how to make it with Bowser:

const browser = bowser.getParser(window.navigator.userAgent);
 
@@ -100,7 +99,7 @@ console.log(impression.userTechData);
   }
 }

Filtering browsers

You could want to filter some particular browsers to provide any special support for them or make any workarounds. It could look like this:

-
const browser = bowser.getParsers(window.navigator.userAgent);
+
const browser = bowser.getParser(window.navigator.userAgent);
 const isValidBrowser = browser.satisfies({
   // declare browsers per OS
   windows: {
@@ -120,6 +119,13 @@ const isValidBrowser = browser.satisfies({
   chrome: ">20.1.1432",
   firefox: ">31",
   opera: ">22"
+
+  // also supports equality operator
+  chrome: "=20.1.1432", // will match particular build only
+
+  // and loose-equality operator
+  chrome: "~20"         // will match any 20.* sub-version
+  chrome: "~20.1"       // will match any 20.1.* sub-version (20.1.19 as well as 20.1.12.42-alpha.1)
 });

Settings for any particular OS or platform has more priority and redefines settings of standalone browsers. Thus, you can define OS or platform specific rules and they will have more priority in the end.

More of API and possibilities you will find in the docs folder.

@@ -144,7 +150,7 @@ check if all tests are still passing.


- Documentation generated by JSDoc 3.5.5 on Sun Jul 22 2018 19:42:18 GMT+0300 (EEST) using the docdash theme. + Documentation generated by JSDoc 3.5.5 on Sat Aug 18 2018 14:14:47 GMT+0300 (EEST) using the docdash theme.
diff --git a/docs/parser.js.html b/docs/parser.js.html index 863ee8a..491d283 100644 --- a/docs/parser.js.html +++ b/docs/parser.js.html @@ -436,6 +436,7 @@ class Parser { compareVersion(version) { let expectedResult = 0; let comparableVersion = version; + let isLoose = false; if (version[0] === '>') { expectedResult = 1; @@ -445,8 +446,12 @@ class Parser { comparableVersion = version.substr(1); } else if (version[0] === '=') { comparableVersion = version.substr(1); + } else if (version[0] === '~') { + isLoose = true; + comparableVersion = version.substr(1); } - return compareVersions(this.getBrowserVersion(), comparableVersion) === expectedResult; + + return compareVersions(this.getBrowserVersion(), comparableVersion, isLoose) === expectedResult; } isOS(osName) { @@ -490,7 +495,7 @@ export default Parser;
- Documentation generated by JSDoc 3.5.5 on Sun Jul 22 2018 19:42:18 GMT+0300 (EEST) using the docdash theme. + Documentation generated by JSDoc 3.5.5 on Sat Aug 18 2018 14:14:47 GMT+0300 (EEST) using the docdash theme.
diff --git a/docs/utils.js.html b/docs/utils.js.html index ac9ba16..5eeb1a0 100644 --- a/docs/utils.js.html +++ b/docs/utils.js.html @@ -107,23 +107,26 @@ * Calculate browser version weight * * @example - * compareVersions(['1.10.2.1', '1.8.2.1.90']) // 1 - * compareVersions(['1.010.2.1', '1.09.2.1.90']); // 1 - * compareVersions(['1.10.2.1', '1.10.2.1']); // 0 - * compareVersions(['1.10.2.1', '1.0800.2']); // -1 + * compareVersions('1.10.2.1', '1.8.2.1.90') // 1 + * compareVersions('1.010.2.1', '1.09.2.1.90'); // 1 + * compareVersions('1.10.2.1', '1.10.2.1'); // 0 + * compareVersions('1.10.2.1', '1.0800.2'); // -1 + * compareVersions('1.10.2.1', '1.10', true); // 0 * * @param {String} versionA versions versions to compare * @param {String} versionB versions versions to compare + * @param {boolean} [isLoose] enable loose comparison * @return {Number} comparison result: -1 when versionA is lower, * 1 when versionA is bigger, 0 when both equal */ /* eslint consistent-return: 1 */ - static compareVersions(versionA, versionB) { + static compareVersions(versionA, versionB, isLoose = false) { // 1) get common precision for both versions, for example for "10.0" and "9" it should be 2 - let precision = Math.max( - Utils.getVersionPrecision(versionA), - Utils.getVersionPrecision(versionB), - ); + const versionAPrecision = Utils.getVersionPrecision(versionA); + const versionBPrecision = Utils.getVersionPrecision(versionB); + + let precision = Math.max(versionAPrecision, versionBPrecision); + let lastPrecision = 0; const chunks = Utils.map([versionA, versionB], (version) => { const delta = precision - Utils.getVersionPrecision(version); @@ -135,14 +138,19 @@ return Utils.map(_version.split('.'), chunk => new Array(20 - chunk.length).join('0') + chunk).reverse(); }); + // adjust precision for loose comparison + if (isLoose) { + lastPrecision = precision - Math.min(versionAPrecision, versionBPrecision); + } + // iterate in reverse order by reversed chunks array precision -= 1; - while (precision >= 0) { + while (precision >= lastPrecision) { // 4) compare: "000000009" > "000000010" = false (but "9" > "10" = true) if (chunks[0][precision] > chunks[1][precision]) { return 1; } else if (chunks[0][precision] === chunks[1][precision]) { - if (precision === 0) { + if (precision === lastPrecision) { // all version chunks are same return 0; } @@ -186,7 +194,7 @@ module.exports = Utils;
- Documentation generated by JSDoc 3.5.5 on Sun Jul 22 2018 19:42:18 GMT+0300 (EEST) using the docdash theme. + Documentation generated by JSDoc 3.5.5 on Sat Aug 18 2018 14:14:47 GMT+0300 (EEST) using the docdash theme.
From 486f718c1d8575fe9bea96a8e48bdc80fb5a2d66 Mon Sep 17 00:00:00 2001 From: Denis Demchenko Date: Sat, 18 Aug 2018 14:20:11 +0300 Subject: [PATCH 4/4] Bump version, write changelog --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 846a71d..5227643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Bowser Changelog +### 2.0.0-beta.1 (August 18, 2018) +- [ADD] Add loose version comparison to `Parser.compareVersion()` and `Parser.satisfies()` +- [CHORE] Add CONTRIBUTING.md +- [DOCS] Regenerate docs + ### 2.0.0-alpha.4 (August 2, 2018) - [DOCS] Fix usage docs (#238) - [CHANGE] Make `./es5.js` the main file of the package (#239) diff --git a/package.json b/package.json index fa2035c..549d2dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bowser", - "version": "2.0.0-alpha.4", + "version": "2.0.0-beta.1", "description": "Lightweight browser detector", "keywords": [ "browser",