diff --git a/CHANGELOG.md b/CHANGELOG.md index 846a71db..52276431 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/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..8b19427d --- /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 ❤️ diff --git a/README.md b/README.md index ee53c41b..dd043900 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/docs/Bowser.html b/docs/Bowser.html index dffd5cfc..fc09b9d9 100644 --- a/docs/Bowser.html +++ b/docs/Bowser.html @@ -559,7 +559,7 @@
The library is made to help to detect what browser your user
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.
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.
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 @@ Browser props detection
Often we need to pick users' browser properti
}
}
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 @@ Browser props detection
Often we need to pick users' browser properti
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 @@ License
Licensed as MIT. All rights not explicitly granted in the MIT
diff --git a/docs/parser.js.html b/docs/parser.js.html
index 863ee8ac..491d2834 100644
--- a/docs/parser.js.html
+++ b/docs/parser.js.html
@@ -436,6 +436,7 @@
parser.js
compareVersion(version) {
let expectedResult = 0;
let comparableVersion = version;
+ let isLoose = false;
if (version[0] === '>') {
expectedResult = 1;
@@ -445,8 +446,12 @@ parser.js
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 @@ parser.js
diff --git a/docs/utils.js.html b/docs/utils.js.html
index ac9ba169..5eeb1a07 100644
--- a/docs/utils.js.html
+++ b/docs/utils.js.html
@@ -107,23 +107,26 @@ utils.js
* 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 @@ utils.js
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 @@ utils.js
diff --git a/package.json b/package.json
index fa2035c1..549d2ddb 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",
diff --git a/src/parser.js b/src/parser.js
index df985402..283f7df4 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 b40a849b..4d313e67 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 afb643ee..5e37a0ba 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 02409762..4d73cbca 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}`);
});
});