From b29a9a7ffbcb0fd0e99c5a49150c2ddb6fa91102 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 28 Feb 2024 10:19:30 +0700 Subject: [PATCH 01/26] Fix #708 - Improve detection for Quest 3 --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7de870b..2bdf786 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -708,7 +708,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ - /(quest( 2| pro)?)/i // Oculus Quest + /(quest( \d| pro)?)/i // Oculus Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ /////////////////// diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 3ccb234..fbaf6e9 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1385,6 +1385,15 @@ "type": "wearable" } }, + { + "desc": "Oculus Quest 3", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest 3) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/31.4.0.6.51.566757996 Chrome/120.0.6099.283 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest 3", + "type": "wearable" + } + }, { "desc": "Oculus Quest Pro", "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest Pro) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/24.4.0.22.60.426469926 SamsungBrowser/4.0 Chrome/106.0.5249.181 VR Safari/537.36", From a43d6595777b5caa510ddf198497187464f7d5f8 Mon Sep 17 00:00:00 2001 From: Dai Jie Date: Tue, 19 Mar 2024 22:32:43 +0800 Subject: [PATCH 02/26] Fix #710: Add type to IBrowser (#711) --- src/main/ua-parser.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 09308fe..6ccf867 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,6 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; + type?: string; } interface ICPU extends IData { From 8dce4cc5146c70239076b0610a1fe241cbcd9fe6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 17 May 2024 22:36:41 +0700 Subject: [PATCH 03/26] Fix #721 - Improve detection: recognize OPPO Pad as tablet --- src/main/ua-parser.js | 2 ++ test/specs/device-all.json | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2bdf786..b00a871 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -521,6 +521,8 @@ /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ + /\b(opd2\d{3}a?) bui\//i + ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ // Vivo /vivo (\w+)(?: bui|\))/i, diff --git a/test/specs/device-all.json b/test/specs/device-all.json index fbaf6e9..3927c2a 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1511,6 +1511,15 @@ "type": "mobile" } }, + { + "desc": "OPPO Pad", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OPD2101", + "type": "tablet" + } + }, { "desc": "OPPO Neo", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; R831T Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 OppoBrowser/3.3.2 Mobile Safari/534.30", From d0db40c2906aba9e33d2a19a9e777ab62bc8956a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 17 May 2024 22:56:52 +0700 Subject: [PATCH 04/26] Fix #722 - Add new browser name: Twitter --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 3 ++- test/specs/browser-all.json | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 8930a27..f7dab4b 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -128,6 +128,7 @@ const Browser = Object.freeze({ TESLA: 'Tesla', TIKTOK: 'TikTok', TIZEN: 'Tizen Browser', + TWITTER: 'Twitter', UC: 'UCBrowser', UP: 'UP.Browser', VIERA: 'Viera', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b00a871..daa9684 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -379,6 +379,7 @@ /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay + /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS @@ -521,7 +522,7 @@ /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ - /\b(opd2\d{3}a?) bui\//i + /\b(opd2\d{3}a?) bui/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ // Vivo diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 471cdb3..815fcc0 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -2071,5 +2071,25 @@ "version" : "12.33.0.36", "major" : "12" } + }, + { + "desc" : "Twitter", + "ua" : "Mozilla/5.0 (Linux; Android 13; CPH2531 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.120 Mobile Safari/537.36 TwitterAndroid", + "expect" : + { + "name" : "Twitter", + "version" : "undefined", + "major" : "undefined" + } + }, + { + "desc" : "Twitter", + "ua" : "Mozilla/5.0 (iPad; CPU OS 15_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/19H12 Twitter for iPhone/10.34", + "expect" : + { + "name" : "Twitter", + "version" : "10.34", + "major" : "10" + } } ] \ No newline at end of file From e87c794fd95628743f25027b58faed00dc8d0cdc Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 23 May 2024 18:12:02 +0700 Subject: [PATCH 05/26] Fix #730 - Improve browser detection: DuckDuckGo --- src/main/ua-parser.js | 2 ++ test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index daa9684..707611f 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -320,6 +320,8 @@ /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ + /\bddg\/([\w\.]+)/i // DuckDuckGo + ], [VERSION, [NAME, 'DuckDuckGo']], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 815fcc0..229da18 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -419,6 +419,16 @@ "major" : "1" } }, + { + "desc" : "DuckDuckGo", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.1517.4.1 Ddg/17.4.1", + "expect" : + { + "name" : "DuckDuckGo", + "version" : "17.4.1", + "major" : "17" + } + }, { "desc" : "DuckDuckGo", "ua" : "Mozilla/5.0 (Linux; Android 8.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile DuckDuckGo/5 Safari/537.36", From 150d3c6b4af498ca754099ab34e9426a02c82ab7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 1 Jun 2024 17:52:12 +0700 Subject: [PATCH 06/26] Add new feature: parse user-agent in CLI using `npx ua-parser-js "[INSERT-UA-HERE]"` and print the result in JSON format --- package.json | 1 + script/cli.js | 4 ++++ 2 files changed, 5 insertions(+) create mode 100755 script/cli.js diff --git a/package.json b/package.json index 09bfd49..5e067dd 100755 --- a/package.json +++ b/package.json @@ -196,6 +196,7 @@ "dist", "src" ], + "bin": "./script/cli.js", "scripts": { "build": "./script/build-dist.sh && ./script/build-module.js", "build+test": "npm run build && npm run test", diff --git a/script/cli.js b/script/cli.js new file mode 100755 index 0000000..c015bd3 --- /dev/null +++ b/script/cli.js @@ -0,0 +1,4 @@ +#!/usr/bin/env node + +const UAParser = require('ua-parser-js'); +console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)))); \ No newline at end of file From 5a8ce350548142081e60e3e2cdf79fb71206254a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 10:38:24 +0700 Subject: [PATCH 07/26] Insert spaces to command line output for readability --- script/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/cli.js b/script/cli.js index c015bd3..785a104 100755 --- a/script/cli.js +++ b/script/cli.js @@ -1,4 +1,4 @@ #!/usr/bin/env node const UAParser = require('ua-parser-js'); -console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)))); \ No newline at end of file +console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)), null, 4)); \ No newline at end of file From 760e85bbe7b55bfac3e2a78c055897ea3968c62b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:03:27 +0700 Subject: [PATCH 08/26] Update test for some missing browsers: Blazer, Comodo Dragon, Conkeror, Go Browser, Iron, Jasmine, Links, NetSurf, OviBrowser, Quark, Rekonq, w3m --- test/specs/browser-all.json | 174 +++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 2 deletions(-) diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 229da18..abff8da 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -209,6 +209,16 @@ "major" : "11" } }, + { + "desc" : "Blazer", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320", + "expect" : + { + "name" : "Blazer", + "version" : "4.0", + "major" : "4" + } + }, { "desc" : "Bolt", "ua" : "Mozilla/5.0 (X11; 78; CentOS; US-en) AppleWebKit/527+ (KHTML, like Gecko) Bolt/0.862 Version/3.0 Safari/523.15", @@ -389,6 +399,26 @@ "major" : "78" } }, + { + "desc" : "Comodo Dragon", + "ua" : "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/535.7 (KHTML, like Gecko) Comodo_Dragon/16.1.1.0 Chrome/16.0.912.63 Safari/535.7", + "expect" : + { + "name" : "Comodo Dragon", + "version" : "16.1.1.0", + "major" : "16" + } + }, + { + "desc" : "Conkeror", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:6.0.1) Gecko/20110831 conkeror/0.9.3", + "expect" : + { + "name" : "conkeror", + "version" : "0.9.3", + "major" : "0" + } + }, { "desc" : "Dillo", "ua" : "Dillo/2.2", @@ -459,6 +489,16 @@ "major" : "5" } }, + { + "desc" : "Go Browser", + "ua" : "NokiaE66/GoBrowser/2.0.297", + "expect" : + { + "name" : "GoBrowser", + "version" : "2.0.297", + "major" : "2" + } + }, { "desc" : "Waterfox", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.2.2 Waterfox/55.2.2", @@ -778,6 +818,26 @@ "major" : "11" } }, + { + "desc" : "Iron", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1250.0 Iron/22.0.2150.0 Safari/537.4", + "expect" : + { + "name" : "Iron", + "version" : "22.0.2150.0", + "major" : "22" + } + }, + { + "desc" : "Jasmine", + "ua" : "SAMSUNG-S8000/S8000XXIF3 SHP/VPP/R5 Jasmine/1.0 Nextreaming SMM-MMS/1.2.0 profile/MIDP-2.1 configuration/CLDC-1.1", + "expect" : + { + "name" : "Jasmine", + "version" : "1.0", + "major" : "1" + } + }, { "desc" : "K-Meleon", "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2", @@ -1018,6 +1078,26 @@ "major" : "6" } }, + { + "desc" : "NetSurf in Plan9", + "ua" : "Mozilla/5.0 (Plan9) NetSurf/3.12", + "expect" : + { + "name" : "NetSurf", + "version" : "3.12", + "major" : "3" + } + }, + { + "desc" : "NetSurf in Linux", + "ua" : "NetSurf/3.10 (Linux; Arch Linux)", + "expect" : + { + "name" : "NetSurf", + "version" : "3.10", + "major" : "3" + } + }, { "desc" : "Nokia Browser", "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/025.007; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.37 Mobile Safari/533.4 3gpp-gba", @@ -1198,6 +1278,16 @@ "major" : "1" } }, + { + "desc" : "OviBrowser", + "ua" : "Mozilla/5.0 (Series40; NokiaX3-02/le6.32; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/1.0.0.11.8", + "expect" : + { + "name" : "OviBrowser", + "version" : "1.0.0.11.8", + "major" : "1" + } + }, { "desc" : "PhantomJS", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.2 Safari/534.34", @@ -1229,7 +1319,7 @@ } }, { - "desc" : "QQ", + "desc" : "QQBrowser", "ua" : "Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; OPPO R7s Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.1 Mobile Safari/537.36", "expect" : { @@ -1238,6 +1328,26 @@ "major" : "7" } }, + { + "desc" : "QQBrowser", + "ua" : "Mozilla/5.0 (Linux; U; Android 9; zh-cn; vivo X21 Build/PKQ1.180819.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.9 Mobile Safari/537.36", + "expect" : + { + "name" : "QQBrowser", + "version" : "9.9", + "major" : "9" + } + }, + { + "desc" : "Quark", + "ua" : "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; JLH-AN00 Build/HONORJLH-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/5.8.2.221 Mobile Safari/537.36", + "expect" : + { + "name" : "Quark", + "version" : "5.8.2.221", + "major" : "5" + } + }, { "desc" : "QupZilla", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.8.9 Safari/538.1", @@ -1248,6 +1358,16 @@ "major" : "1" } }, + { + "desc" : "Rekonq 2", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21", + "expect" : + { + "name" : "rekonq", + "version" : "2.2.1", + "major" : "2" + } + }, { "desc" : "RockMelt", "ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) RockMelt/0.8.36.78 Chrome/7.0.517.44 Safari/534.7", @@ -1479,7 +1599,7 @@ } }, { - "desc" : "UPBrowser", + "desc" : "UP.Browser", "ua" : "BenQ-CF61/1.00/WAP2.0/MIDP2.0/CLDC1.0 UP.Browser/6.3.0.4.c.1.102 (GUI) MMP/2.0", "expect" : { @@ -1558,6 +1678,16 @@ "major" : "undefined" } }, + { + "desc" : "w3m", + "ua" : "w3m/0.5.1", + "expect" : + { + "name" : "w3m", + "version" : "0.5.1", + "major" : "0" + } + }, { "desc" : "Yandex", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5402 Safari/536.5", @@ -1914,6 +2044,46 @@ "name" : "LinkedIn" } }, + { + "desc" : "Links in Linux", + "ua" : "Links (2.xpre7; Linux 2.4.18 i586; x)", + "expect" : + { + "name" : "Links", + "version" : "2.xpre7", + "major" : "2" + } + }, + { + "desc" : "Links in Mac", + "ua" : "Links (2.1pre33; Darwin 8.11.0 Power Macintosh; 169x55)", + "expect" : + { + "name" : "Links", + "version" : "2.1pre33", + "major" : "2" + } + }, + { + "desc" : "Links in NetBSD", + "ua" : "Links (2.29; NetBSD 10.0 i386; GNU C 10.5; x)", + "expect" : + { + "name" : "Links", + "version" : "2.29", + "major" : "2" + } + }, + { + "desc" : "Links in FreeBSD", + "ua" : "Links (2.1pre15; FreeBSD 5.3-RELEASE i386; 196x84)", + "expect" : + { + "name" : "Links", + "version" : "2.1pre15", + "major" : "2" + } + }, { "desc" : "Safari including comma in minor version number", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6,2 Safari/605.1.15", From 6b6fcc68f50514626e224d511242534140b84acb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:10:37 +0700 Subject: [PATCH 09/26] Improve browser detection for Sleipnir --- src/main/ua-parser.js | 7 ++++--- test/specs/browser-all.json | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 707611f..7b4df95 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -309,7 +309,8 @@ /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu ], [VERSION, [NAME, 'Baidu']], [ /(kindle)\/([\w\.]+)/i, // Kindle - /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer + /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, + // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir // Trident based /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer @@ -433,8 +434,8 @@ /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla // Other - /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, - // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser + /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser /(links) \(([\w\.]+)/i, // Links /panasonic;(viera)/i // Panasonic Viera ], [NAME, VERSION], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index abff8da..fd0f183 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1487,6 +1487,37 @@ "version" : "2.0", "major" : "2" } + }, + { + "desc" : "Sleipnir", + "ua" : "Mozilla/5.0 (Linux; Android 10; SOV37 Build/52.1.C.0.220; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.120 Mobile Safari/537.36 Sleipnir/3.7.5", + "expect" : + { + "name" : "Sleipnir", + "version" : "3.7.5", + "major" : "3" + } + }, + + { + "desc" : "Sleipnir", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Sleipnir 2.8.4)", + "expect" : + { + "name" : "Sleipnir", + "version" : "2.8.4", + "major" : "2" + } + }, + { + "desc" : "Sleipnir", + "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) Sleipnir/2.8.4", + "expect" : + { + "name" : "Sleipnir", + "version" : "2.8.4", + "major" : "2" + } }, { "desc" : "SlimBrowser", From 4cd867a36e78391581e771573737276b858348ca Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:14:42 +0700 Subject: [PATCH 10/26] Improve browser detection for Klar https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox#klar_for_android --- src/main/ua-parser.js | 8 ++++---- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7b4df95..fae8f2d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -316,8 +316,8 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo|klar)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ @@ -426,8 +426,8 @@ ], [VERSION, [NAME, FIREFOX+' Reality']], [ /ekiohf.+(flow)\/([\w\.]+)/i, // Flow /(swiftfox)/i, // Swiftfox - /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, - // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, + // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix /(firefox)\/([\w\.]+)/i, // Other Firefox-based diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index fd0f183..972951c 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -858,6 +858,16 @@ "major" : "2" } }, + { + "desc" : "Klar < 4.1", + "ua" : "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Klar/1.0 Chrome/58.0.3029.83 Mobile Safari/537.36", + "expect" : + { + "name" : "Klar", + "version" : "1.0", + "major" : "1" + } + }, { "desc" : "Konqueror", "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux; X11; x86_64) KHTML/3.5.6 (like Gecko) (Kubuntu)", From 1fa3d02594f46ae5c4e96a5b3b6508cc4d9246e0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:20:50 +0700 Subject: [PATCH 11/26] Remove Viera from list of browsers --- src/enums/ua-parser-enums.js | 3 +-- src/main/ua-parser.js | 1 - test/specs/browser-all.json | 10 ---------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index f7dab4b..c070677 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -50,7 +50,7 @@ const Browser = Object.freeze({ FENNEC: 'Fennec', FLOCK: 'Flock', FLOW: 'Flow', - GO: 'Go Browser', + GO: 'GoBrowser', GOOGLE_SEARCH: 'GSA', HEYTAP: 'HeyTap', HUAWEI: 'Huawei Browser', @@ -131,7 +131,6 @@ const Browser = Object.freeze({ TWITTER: 'Twitter', UC: 'UCBrowser', UP: 'UP.Browser', - VIERA: 'Viera', VIVALDI: 'Vivaldi', VIVO: 'Vivo Browser', W3M: 'w3m', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index fae8f2d..80ffc96 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -437,7 +437,6 @@ /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser /(links) \(([\w\.]+)/i, // Links - /panasonic;(viera)/i // Panasonic Viera ], [NAME, VERSION], [ /(cobalt)\/([\w\.]+)/i // Cobalt diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 972951c..191dea9 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1709,16 +1709,6 @@ "major" : "16" } }, - { - "desc" : "Viera", - "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", - "expect" : - { - "name" : "VIERA", - "version" : "undefined", - "major" : "undefined" - } - }, { "desc" : "w3m", "ua" : "w3m/0.5.1", From 85bf7076d3b2f189c7a59586e0832f4b97f8ccaf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:32:23 +0700 Subject: [PATCH 12/26] Improve browser detection for ICEBrowser --- src/main/ua-parser.js | 4 ++-- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 80ffc96..bc66b7a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -436,8 +436,8 @@ // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i, // Links - ], [NAME, VERSION], [ + /(links) \(([\w\.]+)/i // Links + ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 191dea9..db9b849 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -758,6 +758,16 @@ "major" : "2" } }, + { + "desc" : "ICEBrowser", + "ua" : "Mozilla/5.0 (Java 1.6.0_01; Windows XP 5.1 x86; en) ICEbrowser/v6_1_2", + "expect" : + { + "name" : "ICEbrowser", + "version" : "6.1.2", + "major" : "6" + } + }, { "desc" : "IceCat", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092921 IceCat/3.0.3-g1", From 12c2c2e48adf7d271b89213768c224b1b776e89f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:35:33 +0700 Subject: [PATCH 13/26] Improve browser detection for Rekonq --- src/main/ua-parser.js | 2 +- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index bc66b7a..7f4c3ff 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -369,7 +369,7 @@ /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser ], [NAME, VERSION], [ - /(lbbrowser)/i, // LieBao Browser + /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq /\[(linkedin)app\]/i // LinkedIn App for iOS & Android ], [NAME], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index db9b849..d90c4c5 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -898,6 +898,16 @@ "major" : "5" } }, + { + "desc" : "Rekonq", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ) AppleWebKit/533.3 (KHTML, like Gecko) rekonq Safari/533.3", + "expect" : + { + "name" : "rekonq", + "version" : "undefined", + "major" : "undefined" + } + }, { "desc" : "Smart Lenovo Browser", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.0.10171 SLBChan/8", From 1a2ef00509bd72ca18854baeb64dbdc64a8bc64d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:49:27 +0700 Subject: [PATCH 14/26] Improve browser detection for QQBrowser --- src/main/ua-parser.js | 4 +++- test/specs/browser-all.json | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7f4c3ff..b0b3de0 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -316,7 +316,7 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo|klar)\/([-\w\.]+)/i, + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo @@ -355,6 +355,8 @@ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ + /\b(qq)\/([\w\.]+)/i // QQ + ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index d90c4c5..77aca1b 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1944,7 +1944,7 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Mobile/14A456 QQ/6.5.3.410 V1_IPH_SQ_6.5.3_1_APP_A Pixel/1080 Core/UIWebView NetType/WIFI Mem/26", "expect" : { - "name" : "QQ", + "name" : "QQBrowser", "version" : "6.5.3.410", "major" : "6" } @@ -1954,7 +1954,7 @@ "ua" : "Mozilla/5.0 (Linux; Android 6.0; PRO 6 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile MQQBrowser/6.8 TBS/036824 Safari/537.36 V1_AND_SQ_6.5.8_422_YYB_D PA QQ/6.5.8.2910 NetType/WIFI WebP/0.3.0 Pixel/1080", "expect" : { - "name" : "QQ", + "name" : "QQBrowser", "version" : "6.5.8.2910", "major" : "6" } From 8991d34e56630bb7d0daf294bba942a1a262733f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 21:21:53 +0700 Subject: [PATCH 15/26] Update `formFactor` -> `formFactors`, in accordance to the latest change in client hints spec --- src/main/ua-parser.js | 20 ++++++++++---------- test/mocha-test.js | 8 ++++---- test/playwright-test-main.spec.mjs | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b0b3de0..693ee88 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -42,7 +42,7 @@ USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, BRANDS = 'brands', - FORMFACTOR = 'formFactor', + FORMFACTORS = 'formFactors', FULLVERLIST = 'fullVersionList', PLATFORM = 'platform', PLATFORMVER = 'platformVersion', @@ -51,12 +51,12 @@ CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, - CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', + CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', @@ -269,7 +269,7 @@ 'RT' : 'ARM' }, - formFactorMap = { + formFactorsMap = { 'embedded' : 'Automotive', 'mobile' : 'Mobile', 'tablet' : ['Tablet', 'EInk'], @@ -975,7 +975,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], - [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], + [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1095,15 +1095,15 @@ this.set(TYPE, CONSOLE) .set(VENDOR, MICROSOFT); } - if (uaCH[FORMFACTOR]) { + if (uaCH[FORMFACTORS]) { var ff; - if (typeof uaCH[FORMFACTOR] !== 'string') { + if (typeof uaCH[FORMFACTORS] !== 'string') { var idx = 0; - while (!ff && idx < uaCH[FORMFACTOR].length) { - ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); + while (!ff && idx < uaCH[FORMFACTORS].length) { + ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap); } } else { - ff = strMapper(uaCH[FORMFACTOR], formFactorMap); + ff = strMapper(uaCH[FORMFACTORS], formFactorsMap); } this.set(TYPE, ff); } diff --git a/test/mocha-test.js b/test/mocha-test.js index 0de0efe..6241383 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -463,18 +463,18 @@ describe('Map UA-CH headers', function () { }); }); - it('Can detect form-factor from client-hints', function () { + it('Can detect form-factors from client-hints', function () { const FFVR = { - 'sec-ch-ua-form-factor' : '"VR"' + 'sec-ch-ua-form-factors' : '"VR"' }; const FFEInk = { - 'sec-ch-ua-form-factor' : '"Tablet", "EInk"' + 'sec-ch-ua-form-factors' : '"Tablet", "EInk"' }; const FFUnknown = { - 'sec-ch-ua-form-factor' : '"Unknown"' + 'sec-ch-ua-form-factors' : '"Unknown"' }; UAParser(FFVR).withClientHints().then(function (ua) { diff --git a/test/playwright-test-main.spec.mjs b/test/playwright-test-main.spec.mjs index 982e942..e830bbf 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/playwright-test-main.spec.mjs @@ -40,7 +40,7 @@ test('read client hints data', async ({ page }) => { } ], platform: 'New OS', - formFactor: 'New Form Factor' + formFactors: 'New Form Factor' }); } } From 1a22c6951f725a16af7bc2c99fc60f2c9e2d6869 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 22:28:58 +0700 Subject: [PATCH 16/26] Update all package references in /test to use current working directories --- test/jazzer-fuzz-test.js | 2 +- test/mocha-test-es6.mjs | 4 ++-- test/mocha-test-extension.js | 4 ++-- test/mocha-test-helpers.js | 4 ++-- test/mocha-test.js | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/jazzer-fuzz-test.js b/test/jazzer-fuzz-test.js index ee30cf2..8d87049 100644 --- a/test/jazzer-fuzz-test.js +++ b/test/jazzer-fuzz-test.js @@ -1,5 +1,5 @@ const { FuzzedDataProvider } = require('@jazzer.js/core'); -const UAParser = require('ua-parser-js'); +const { UAParser } = require('../src/main/ua-parser'); const UA_MAX_LENGTH = 350; module.exports.fuzz = function (buffer) { diff --git a/test/mocha-test-es6.mjs b/test/mocha-test-es6.mjs index 54c4dca..c0bd217 100644 --- a/test/mocha-test-es6.mjs +++ b/test/mocha-test-es6.mjs @@ -1,5 +1,5 @@ -import { UAParser } from 'ua-parser-js'; -import { CPU, Device, Engine } from 'ua-parser-js/enums'; +import { UAParser } from '../src/main/ua-parser.mjs'; +import { CPU, Device, Engine } from '../src/enums/ua-parser-enums.mjs'; import * as assert from 'assert'; describe('Returns', () => { diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index fd29fd8..45418ac 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -3,8 +3,8 @@ const assert = require('assert'); const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); -const UAParser = require('ua-parser-js'); -const { Bots, CLIs, Emails, Modules } = require('ua-parser-js/extensions'); +const { UAParser } = require('../src/main/ua-parser'); +const { Bots, CLIs, Emails, Modules } = require('../src/extensions/ua-parser-extensions'); describe('Bots', () => { it('Can detect bots', () => { diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index 9e27d89..faacf61 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,6 +1,6 @@ const assert = require('assert'); -const UAParser = require('ua-parser-js'); -const { isAppleSilicon, isChromiumBased } = require('ua-parser-js/helpers'); +const { UAParser } = require('../src/main/ua-parser'); +const { isAppleSilicon, isChromiumBased } = require('../src/helpers/ua-parser-helpers'); describe('isAppleSilicon', () => { it('Can detect Apple Silicon device', () => { diff --git a/test/mocha-test.js b/test/mocha-test.js index 6241383..2de9d87 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -4,7 +4,7 @@ var assert = require('assert'); var requirejs = require('requirejs'); var parseJS = require('@babel/parser').parse; var traverse = require('@babel/traverse').default; -var UAParser = require('ua-parser-js'); +var {UAParser} = require('../src/main/ua-parser'); var browsers = require('./specs/browser-all.json'); var cpus = require('./specs/cpu-all.json'); var devices = require('./specs/device-all.json'); From 39590f112dd96c3b173caaa889f407a510d72200 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 23:04:25 +0700 Subject: [PATCH 17/26] BREAKING CHANGE - Add new property to `browser`: `type` --- src/enums/ua-parser-enums.js | 11 ++++++++++- src/extensions/ua-parser-extensions.js | 14 +++++++------- src/main/ua-parser.d.ts | 1 + src/main/ua-parser.js | 4 ++-- test/dts-test.ts | 1 + test/mocha-test-es6.mjs | 3 +-- test/mocha-test-extension.js | 2 +- test/mocha-test.js | 3 ++- 8 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index c070677..692fe0e 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -144,6 +144,14 @@ const Browser = Object.freeze({ // TODO : test! }); +const BrowserType = Object.freeze({ + BOT: 'bot', + CLI: 'cli', + EMAIL: 'email', + INAPP: 'inapp', + MODULE: 'module' +}); + const CPU = Object.freeze({ ARM : 'arm', ARM_64: 'arm64', @@ -341,7 +349,8 @@ const OS = Object.freeze({ }); module.exports = { - Browser, + Browser, + BrowserType, CPU, Device, Vendor, diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 777d227..9d73878 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -15,12 +15,6 @@ const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; -const Apps = Object.freeze({ - browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] - ] -}); - const Bots = Object.freeze({ browser : [ // Googlebot / BingBot / MSNBot / FacebookBot @@ -124,6 +118,12 @@ const Emails = Object.freeze({ ] }); +const InApps = Object.freeze({ + browser : [ + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'inapp']] + ] +}); + const MediaPlayers = Object.freeze({ browser : [[ @@ -238,11 +238,11 @@ const Modules = Object.freeze({ }); module.exports = { - Apps, Bots, CLIs, ExtraDevices, Emails, + InApps, MediaPlayers, Modules }; \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 6ccf867..b37c67c 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -62,6 +62,7 @@ declare namespace UAParser { NAME: 'name'; VERSION: 'version'; MAJOR: 'major'; + TYPE: 'type'; }; static readonly CPU: { ARCHITECTURE: 'architecture'; diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 693ee88..2217759 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -847,7 +847,7 @@ var defaultProps = (function () { var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; setProps.call(props.init, [ - [UA_BROWSER, [NAME, VERSION, MAJOR]], + [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_ENGINE, [NAME, VERSION]], @@ -1225,7 +1225,7 @@ } UAParser.VERSION = LIBVERSION; - UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); + UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]); UAParser.CPU = enumerize([ARCHITECTURE]); UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); diff --git a/test/dts-test.ts b/test/dts-test.ts index 6ede700..f181c6b 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,6 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); +expectType(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); diff --git a/test/mocha-test-es6.mjs b/test/mocha-test-es6.mjs index c0bd217..69a334e 100644 --- a/test/mocha-test-es6.mjs +++ b/test/mocha-test-es6.mjs @@ -7,8 +7,7 @@ describe('Returns', () => { assert.deepEqual(new UAParser('').getResult(), { ua : '', - //ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, - browser: { name: undefined, version: undefined, major: undefined }, + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, engine: { name: undefined, version: undefined}, diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 45418ac..939802f 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -26,7 +26,7 @@ describe('Bots', () => { assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"}); + assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8", type: undefined}); // try merging Bots & CLIs const botsAndCLIs = { browser : [...Bots.browser, ...CLIs.browser]}; diff --git a/test/mocha-test.js b/test/mocha-test.js index 2de9d87..184a847 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -10,6 +10,7 @@ var cpus = require('./specs/cpu-all.json'); var devices = require('./specs/device-all.json'); var engines = require('./specs/engine-all.json'); var os = require('./specs/os-all.json'); + var parser = new UAParser(); var methods = [ { @@ -82,7 +83,7 @@ describe('Returns', function () { assert.deepEqual(new UAParser('').getResult(), { ua : '', - browser: { name: undefined, version: undefined, major: undefined }, + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, engine: { name: undefined, version: undefined}, From 0543b87c02de69f55dfbbc86b619b8448749bf81 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 5 Jun 2024 15:47:27 +0700 Subject: [PATCH 18/26] BREAKING CHANGE: AR/VR devices moved to new device type: `xr` --- src/enums/ua-parser-enums.js | 3 ++- src/main/ua-parser.d.ts | 3 ++- src/main/ua-parser.js | 15 +++++++++++---- test/mocha-test.js | 2 +- test/specs/device-all.json | 8 ++++---- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 692fe0e..d994a38 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -179,7 +179,8 @@ const Device = Object.freeze({ MOBILE: 'mobile', SMARTTV: 'smarttv', TABLET: 'tablet', - WEARABLE: 'wearable' + WEARABLE: 'wearable', + XR: 'xr' }); const Vendor = Object.freeze({ diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index b37c67c..9f06b21 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -23,7 +23,7 @@ declare namespace UAParser { } interface IDevice extends IData { - type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable'; + type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable' | 'xr' | 'embedded'; vendor?: string; model?: string; } @@ -76,6 +76,7 @@ declare namespace UAParser { SMARTTV: 'smarttv'; TABLET: 'tablet'; WEARABLE: 'wearable'; + XR: 'xr'; EMBEDDED: 'embedded'; }; static readonly ENGINE: { diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2217759..26038e1 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -38,6 +38,7 @@ TABLET = 'tablet', SMARTTV = 'smarttv', WEARABLE = 'wearable', + XR = 'xr', EMBEDDED = 'embedded', USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, @@ -274,7 +275,8 @@ 'mobile' : 'Mobile', 'tablet' : ['Tablet', 'EInk'], 'smarttv' : 'TV', - 'wearable' : ['VR', 'XR', 'Watch'], + 'wearable' : 'Watch', + 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined }; @@ -711,12 +713,17 @@ ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ - /droid.+; (glass) \d/i // Google Glass - ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ + + /////////////////// + // XR + /////////////////// + + /droid.+; (glass) \d/i // Google Glass + ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ /(quest( \d| pro)?)/i // Oculus Quest - ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// // EMBEDDED diff --git a/test/mocha-test.js b/test/mocha-test.js index 184a847..ead59a8 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -479,7 +479,7 @@ describe('Map UA-CH headers', function () { }; UAParser(FFVR).withClientHints().then(function (ua) { - assert.strictEqual(ua.device.type, 'wearable'); + assert.strictEqual(ua.device.type, 'xr'); }); UAParser(FFEInk).withClientHints().then(function (ua) { diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 3927c2a..252782c 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1373,7 +1373,7 @@ "expect": { "vendor": "Facebook", "model": "Quest", - "type": "wearable" + "type": "xr" } }, { @@ -1382,7 +1382,7 @@ "expect": { "vendor": "Facebook", "model": "Quest 2", - "type": "wearable" + "type": "xr" } }, { @@ -1391,7 +1391,7 @@ "expect": { "vendor": "Facebook", "model": "Quest 3", - "type": "wearable" + "type": "xr" } }, { @@ -1400,7 +1400,7 @@ "expect": { "vendor": "Facebook", "model": "Quest Pro", - "type": "wearable" + "type": "xr" } }, { From f7810dbfcfda55784180e1946987f8741cb24fcf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 14:27:03 +0700 Subject: [PATCH 19/26] Add new browsers: Wolvic & Pico Browser --- src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 6 ++++-- test/specs/browser-all.json | 30 ++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index d994a38..27afb2e 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -104,6 +104,7 @@ const Browser = Object.freeze({ PALEMOON: 'PaleMoon', PHANTOMJS: 'PhantomJS', PHOENIX: 'Phoenix', + PICOBROWSER: 'Pico Browser', POLARIS: 'Polaris', PUFFIN: 'Puffin', QQ: 'QQBrowser', @@ -139,6 +140,7 @@ const Browser = Object.freeze({ WECHAT: 'WeChat', WEIBO: 'Weibo', WHALE: 'Whale', + WOLVIC: 'Wolvic', YANDEX: 'Yandex' // TODO : test! diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 26038e1..d97570a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -359,8 +359,8 @@ ], [[NAME, '360' + SUFFIX_BROWSER]], [ /\b(qq)\/([\w\.]+)/i // QQ ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ - /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser + /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon @@ -426,6 +426,8 @@ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ + /(wolvic)\/([\w\.]+)/i // Wolvic + ], [NAME, VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality ], [VERSION, [NAME, FIREFOX+' Reality']], [ /ekiohf.+(flow)\/([\w\.]+)/i, // Flow diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 77aca1b..c7b3f6c 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -898,6 +898,26 @@ "major" : "5" } }, + { + "desc" : "PicoBrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", + "expect" : + { + "name" : "PicoBrowser", + "version" : "3.3.22", + "major" : "3" + } + }, + { + "desc" : "PicoBrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect" : + { + "name" : "PicoBrowser", + "version" : "3.3.22", + "major" : "3" + } + }, { "desc" : "Rekonq", "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ) AppleWebKit/533.3 (KHTML, like Gecko) rekonq Safari/533.3", @@ -1739,6 +1759,16 @@ "major" : "0" } }, + { + "desc" : "Wolvic", + "ua" : "Mozilla/5.0 (Android 12; Mobile VR; rv:121.0) Gecko/121.0 Firefox/121.0 Wolvic/1.6.1", + "expect" : + { + "name" : "Wolvic", + "version" : "1.6.1", + "major" : "1" + } + }, { "desc" : "Yandex", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5402 Safari/536.5", From 0a46ac396a351ce168d5e459e0daf9671b6a2035 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 20:02:22 +0700 Subject: [PATCH 20/26] Fix #718 - Extension param now accept multiple extensions --- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 27 ++++++++++++++++++--------- test/mocha-test-extension.js | 6 ++++++ test/mocha-test.js | 7 +++++++ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 9f06b21..7af0dfd 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -49,7 +49,7 @@ declare namespace UAParser { type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; - type UAParserExt = Partial>; + type UAParserExt = Partial> | Partial>[]; export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record): IResult; export function UAParser(uastring?: string, headers?: Record): IResult; diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d97570a..eda9ba4 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -101,12 +101,21 @@ // Helper ////////// - var extend = function (regexes, extensions) { - var mergedRegexes = {}; - for (var i in regexes) { - mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; + var extend = function (defaultRgx, extensions) { + var mergedRgx = {}; + var extraRgx = extensions; + if (!isExtensions(extensions)) { + extraRgx = {}; + for (var i in extensions) { + for (var j in extensions[i]) { + extraRgx[j] = extensions[i][j].concat(extraRgx[j] ? extraRgx[j] : []); + } + } } - return mergedRegexes; + for (var k in defaultRgx) { + mergedRgx[k] = extraRgx[k] && extraRgx[k].length % 2 === 0 ? extraRgx[k].concat(defaultRgx[k]) : defaultRgx[k]; + } + return mergedRgx; }, enumerize = function (arr) { var enums = {}; @@ -124,9 +133,9 @@ } return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, - isExtensions = function (obj) { + isExtensions = function (obj, deep) { for (var prop in obj) { - return /^(browser|cpu|device|engine|os)$/.test(prop); + return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false); } }, isString = function (val) { @@ -1163,7 +1172,7 @@ function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { - if (isExtensions(ua)) { + if (isExtensions(ua, true)) { if (typeof extensions === OBJ_TYPE) { headers = extensions; // case UAParser(extensions, headers) } @@ -1173,7 +1182,7 @@ extensions = undefined; } ua = undefined; - } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { + } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { headers = extensions; // case UAParser(ua, headers) extensions = undefined; } diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 939802f..6c66502 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -34,6 +34,12 @@ describe('Bots', () => { assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + // alternative merge options + const botsAndCLIsParser2 = new UAParser([Bots, CLIs]); + const botsAndCLIsParser3 = new UAParser(facebookBot, [Bots, CLIs]); + assert.deepEqual(botsAndCLIsParser2.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); + assert.deepEqual(botsAndCLIsParser3.getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + const emailParser = new UAParser(Emails); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); diff --git a/test/mocha-test.js b/test/mocha-test.js index ead59a8..7b5e711 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -126,6 +126,13 @@ describe('Extending Regex', function () { }); let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; assert.deepEqual(myParser2.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}); + + let myParser3 = new UAParser([{ + browser: myOwnListOfBrowsers + }, { + device: myOwnListOfDevices + }]); + assert.deepEqual(myParser3.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}); }); describe('User-agent length', function () { From 5190905df8c14badcd229541cbe3302b61b90779 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 21:52:16 +0700 Subject: [PATCH 21/26] Clean up & few changes related to browser.type --- src/extensions/ua-parser-extensions.d.ts | 2 +- src/main/ua-parser.d.ts | 2 +- test/dts-test.ts | 2 +- test/specs/browser-all.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 5f3a5e3..4be4e30 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -4,10 +4,10 @@ import type { UAParserExt } from "../main/ua-parser"; -export const Apps: UAParserExt; export const Bots: UAParserExt; export const CLIs: UAParserExt; export const ExtraDevices: UAParserExt; export const Emails: UAParserExt; +export const InApps: UAParserExt; export const MediaPlayers: UAParserExt; export const Modules: UAParserExt; \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 7af0dfd..2a44d93 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,7 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; - type?: string; + type?: 'bot' | 'cli' | 'email' | 'inapp' | 'module'; } interface ICPU extends IData { diff --git a/test/dts-test.ts b/test/dts-test.ts index f181c6b..a943490 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,7 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); -expectType(browser.type); +expectType<'bot' | 'cli' | 'email' | 'inapp' | 'module' | undefined>(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index c7b3f6c..a9b22df 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -903,7 +903,7 @@ "ua" : "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", "expect" : { - "name" : "PicoBrowser", + "name" : "Pico Browser", "version" : "3.3.22", "major" : "3" } @@ -913,7 +913,7 @@ "ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", "expect" : { - "name" : "PicoBrowser", + "name" : "Pico Browser", "version" : "3.3.22", "major" : "3" } From 173325faa182777e212c8f5a9ae087a7df346f86 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 22:36:15 +0700 Subject: [PATCH 22/26] Add some well-known bot user-agents: Applebot, Amazonbot, Bytespider, Claudebot, Yandexbot --- src/extensions/ua-parser-extensions.js | 28 ++++++++++++++++++-------- test/mocha-test-extension.js | 16 +++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 9d73878..8e416c1 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -14,24 +14,36 @@ const VENDOR = 'vendor'; const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; +const BOT = 'bot'; +const CLI = 'cli'; +const EMAIL = 'email'; +const INAPP = 'inapp'; +const MODULE = 'module'; const Bots = Object.freeze({ browser : [ // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], - // GPTBot - https://platform.openai.com/docs/gptbot - [/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + // YandexBot - https://yandex.com/bots + // Applebot - http://apple.com/go/applebot + // Amazonbot - https://developer.amazon.com/amazonbot + [/((?:google|bing|msn|facebook|gpt|yandex|apple|amazon)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], + + // ClaudeBot / Bytespider + [/(claude(?:bot|-web)|bytespider)\/?([\w\.]*)/i], [NAME, VERSION, [TYPE, BOT]], + + // Yandex Bots - https://yandex.com/bots + [/http:\/\/(yandex).com\/(bot)s/i], [NAME, TYPE] ] }); const CLIs = Object.freeze({ browser : [ // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); @@ -114,13 +126,13 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ // Microsoft Outlook / Thunderbird - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); const InApps = Object.freeze({ browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'inapp']] + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] ] }); @@ -233,7 +245,7 @@ const MediaPlayers = Object.freeze({ const Modules = Object.freeze({ browser : [ // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] ] }); diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 6c66502..ca2251b 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -15,6 +15,14 @@ describe('Bots', () => { const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)'; const wget = 'Wget/1.21.1'; const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; + const yandexBot = 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)'; + const yandexMobileScreenShotBot ='Mozilla/5.0 (compatible; YandexMobileScreenShotBot/1.0; +http://yandex.com/bots)'; + const appleBot = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)'; + const amazonBot = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML\, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)'; + const claudeBot = 'ClaudeBot'; + const claudeBot2 = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; + const claudeWeb = 'Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)'; + const bytespider = 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider'; const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; const axios = 'axios/1.3.5'; @@ -25,6 +33,14 @@ describe('Bots', () => { assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(yandexBot).getBrowser(), {name: "YandexBot", version: "3.0", major: "3", type: "bot"}); + assert.deepEqual(botParser.setUA(yandexMobileScreenShotBot).getBrowser(), {name: "yandex", version: undefined, major: undefined, type: "bot"}); + assert.deepEqual(botParser.setUA(appleBot).getBrowser(), {name: "Applebot", version: "0.1", major: "0", type: "bot"}); + assert.deepEqual(botParser.setUA(amazonBot).getBrowser(), {name: "Amazonbot", version: "0.1", major: "0", type: "bot"}); + assert.deepEqual(botParser.setUA(claudeBot).getBrowser(), {name: "ClaudeBot", version: undefined, major: undefined, type: "bot"}); + assert.deepEqual(botParser.setUA(claudeBot2).getBrowser(), {name: "ClaudeBot", version: "1.0", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(claudeWeb).getBrowser(), {name: "Claude-Web", version: "1.0", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(bytespider).getBrowser(), {name: "Bytespider", version: undefined, major: undefined, type: "bot"}); assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8", type: undefined}); From db3423a76c0b56400216cc27701d47c89c6b65a4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 7 Jun 2024 23:59:24 +0700 Subject: [PATCH 23/26] BREAKING - Remove `bot` type, divide as `crawler` / `fetcher` Add new crawler: Baiduspider, DuckDuckBot, & Sogou Web Spider Add new fetcher: Mastodon, Pinterestbot, Redditbot, LinkedInBot, Discordbot, Telegrambot, Twitterbot, Snapchat Bot, WhatsApp --- src/enums/ua-parser-enums.js | 3 +- src/extensions/ua-parser-extensions.d.ts | 3 +- src/extensions/ua-parser-extensions.js | 98 ++++++++++++++++++------ src/main/ua-parser.d.ts | 2 +- test/dts-test.ts | 2 +- 5 files changed, 81 insertions(+), 27 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 27afb2e..ea1ce87 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -147,9 +147,10 @@ const Browser = Object.freeze({ }); const BrowserType = Object.freeze({ - BOT: 'bot', + CRAWLER: 'crawler', CLI: 'cli', EMAIL: 'email', + FETCHER: 'fetcher', INAPP: 'inapp', MODULE: 'module' }); diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 4be4e30..893f347 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -4,10 +4,11 @@ import type { UAParserExt } from "../main/ua-parser"; -export const Bots: UAParserExt; export const CLIs: UAParserExt; +export const Crawlers: UAParserExt; export const ExtraDevices: UAParserExt; export const Emails: UAParserExt; +export const Fetchers: UAParserExt; export const InApps: UAParserExt; export const MediaPlayers: UAParserExt; export const Modules: UAParserExt; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 8e416c1..0903ff3 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -14,36 +14,59 @@ const VENDOR = 'vendor'; const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; -const BOT = 'bot'; +const CRAWLER = 'crawler'; const CLI = 'cli'; const EMAIL = 'email'; +const FETCHER = 'fetcher'; const INAPP = 'inapp'; const MODULE = 'module'; -const Bots = Object.freeze({ +const CLIs = Object.freeze({ browser : [ - // Googlebot / BingBot / MSNBot / FacebookBot - // GPTBot - https://platform.openai.com/docs/gptbot - // YandexBot - https://yandex.com/bots - // Applebot - http://apple.com/go/applebot - // Amazonbot - https://developer.amazon.com/amazonbot - [/((?:google|bing|msn|facebook|gpt|yandex|apple|amazon)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], - - // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], - - // ClaudeBot / Bytespider - [/(claude(?:bot|-web)|bytespider)\/?([\w\.]*)/i], [NAME, VERSION, [TYPE, BOT]], - - // Yandex Bots - https://yandex.com/bots - [/http:\/\/(yandex).com\/(bot)s/i], [NAME, TYPE] + // wget / curl / lynx + [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); -const CLIs = Object.freeze({ +const Crawlers = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] + // Amazonbot - https://developer.amazon.com/amazonbot + // Applebot - http://apple.com/go/applebot + // Bingbot - http://www.bing.com/bingbot.htm + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ + // GPTBot - https://platform.openai.com/docs/gptbot + [/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + [/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Bytespider + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + [/((?:bytespider|(?=yahoo! )slurp))/i], + [NAME, [TYPE, CRAWLER]], + + // ClaudeBot + [/(claude(?:bot|-web))\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Googlebot - http://www.google.com/bot.html + [ + /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Sogou Spider + [/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]] ] }); @@ -125,11 +148,39 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird + // Microsoft Outlook / Thunderbird [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); +const Fetchers = Object.freeze({ + browser : [ + // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot + [/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Google Bots / Snapchat + [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [NAME, [TYPE, FETCHER]], + + + // Slackbot - https://api.slack.com/robots + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // WhatsApp + [/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(yandex(?:sitelinks|userproxy))/i + ], + [NAME, VERSION, [TYPE, FETCHER]] + ] +}); + const InApps = Object.freeze({ browser : [ [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] @@ -244,16 +295,17 @@ const MediaPlayers = Object.freeze({ const Modules = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy + // Axios/jsdom/Scrapy [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] ] }); module.exports = { - Bots, CLIs, + Crawlers, ExtraDevices, Emails, + Fetchers, InApps, MediaPlayers, Modules diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 2a44d93..0c35946 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,7 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; - type?: 'bot' | 'cli' | 'email' | 'inapp' | 'module'; + type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module'; } interface ICPU extends IData { diff --git a/test/dts-test.ts b/test/dts-test.ts index a943490..d8a8fc2 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,7 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); -expectType<'bot' | 'cli' | 'email' | 'inapp' | 'module' | undefined>(browser.type); +expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module' | undefined>(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); From bdcd927304d73c4df7645f6c515f42d8f870989f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Jun 2024 00:07:16 +0700 Subject: [PATCH 24/26] Update test for extensions --- package.json | 2 +- src/extensions/ua-parser-extensions.js | 32 +++++ test/mocha-test-extension.js | 170 ++++++++++++------------- test/specs/browser-clis.json | 42 ++++++ test/specs/browser-crawlers.json | 92 +++++++++++++ test/specs/browser-emails.json | 12 ++ test/specs/browser-fetchers.json | 12 ++ test/specs/browser-modules.json | 12 ++ 8 files changed, 284 insertions(+), 90 deletions(-) create mode 100644 test/specs/browser-clis.json create mode 100644 test/specs/browser-crawlers.json create mode 100644 test/specs/browser-emails.json create mode 100644 test/specs/browser-fetchers.json create mode 100644 test/specs/browser-modules.json diff --git a/package.json b/package.json index 5e067dd..b4d3362 100755 --- a/package.json +++ b/package.json @@ -206,7 +206,7 @@ "test:eslint": "eslint src && eslint script", "test:jshint": "jshint src/main", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", - "test:mocha": "mocha -R list test/mocha*js", + "test:mocha": "mocha test/mocha*js", "test:playwright": "playwright test" }, "devDependencies": { diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 0903ff3..760f5aa 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -21,6 +21,10 @@ const FETCHER = 'fetcher'; const INAPP = 'inapp'; const MODULE = 'module'; +////////////////////// +// COMMAND LINE APPS +///////////////////// + const CLIs = Object.freeze({ browser : [ // wget / curl / lynx @@ -28,6 +32,10 @@ const CLIs = Object.freeze({ ] }); +//////////////////////// +// CRAWLERS / SPIDERS +/////////////////////// + const Crawlers = Object.freeze({ browser : [ // Amazonbot - https://developer.amazon.com/amazonbot @@ -70,6 +78,10 @@ const Crawlers = Object.freeze({ ] }); +////////////////// +// EXTRA DEVICES +///////////////// + const ExtraDevices = Object.freeze({ device : [[ /(nook)[\w ]+build\/(\w+)/i, // Nook @@ -146,6 +158,10 @@ const ExtraDevices = Object.freeze({ ] }); +/////////////// +// EMAIL APPS +////////////// + const Emails = Object.freeze({ browser : [ // Microsoft Outlook / Thunderbird @@ -153,6 +169,10 @@ const Emails = Object.freeze({ ] }); +/////////////////////// +// ON-DEMAND SCRAPERS +////////////////////// + const Fetchers = Object.freeze({ browser : [ // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot @@ -181,12 +201,20 @@ const Fetchers = Object.freeze({ ] }); +//////////////////// +// IN-APP BROWSERS +/////////////////// + const InApps = Object.freeze({ browser : [ [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] ] }); +////////////////////// +// MEDIA PLAYER APPS +///////////////////// + const MediaPlayers = Object.freeze({ browser : [[ @@ -293,6 +321,10 @@ const MediaPlayers = Object.freeze({ ] }); +//////////////////////// +// MODULES / LIBRARIES +/////////////////////// + const Modules = Object.freeze({ browser : [ // Axios/jsdom/Scrapy diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index ca2251b..c2ee99d 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -4,98 +4,90 @@ const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); const { UAParser } = require('../src/main/ua-parser'); -const { Bots, CLIs, Emails, Modules } = require('../src/extensions/ua-parser-extensions'); +const clis = require('./specs/browser-clis.json'); +const crawlers = require('./specs/browser-crawlers.json'); +const emails = require('./specs/browser-emails.json'); +const fetchers = require('./specs/browser-fetchers.json'); +const modules = require('./specs/browser-modules.json'); +const { CLIs, Crawlers, Emails, Fetchers, Modules } = require('../src/extensions/ua-parser-extensions'); -describe('Bots', () => { - it('Can detect bots', () => { - const googleBot = 'Googlebot-Video/1.0'; - const gptBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)'; - const msnBot = 'msnbot-media/1.1 (+http://search.msn.com/msnbot.htm)'; - const bingPreview = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b'; - const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)'; - const wget = 'Wget/1.21.1'; - const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; - const yandexBot = 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)'; - const yandexMobileScreenShotBot ='Mozilla/5.0 (compatible; YandexMobileScreenShotBot/1.0; +http://yandex.com/bots)'; - const appleBot = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)'; - const amazonBot = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML\, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)'; - const claudeBot = 'ClaudeBot'; - const claudeBot2 = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; - const claudeWeb = 'Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)'; - const bytespider = 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider'; - const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; - const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; - const axios = 'axios/1.3.5'; - const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; - const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; - - const botParser = new UAParser(Bots); - assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(yandexBot).getBrowser(), {name: "YandexBot", version: "3.0", major: "3", type: "bot"}); - assert.deepEqual(botParser.setUA(yandexMobileScreenShotBot).getBrowser(), {name: "yandex", version: undefined, major: undefined, type: "bot"}); - assert.deepEqual(botParser.setUA(appleBot).getBrowser(), {name: "Applebot", version: "0.1", major: "0", type: "bot"}); - assert.deepEqual(botParser.setUA(amazonBot).getBrowser(), {name: "Amazonbot", version: "0.1", major: "0", type: "bot"}); - assert.deepEqual(botParser.setUA(claudeBot).getBrowser(), {name: "ClaudeBot", version: undefined, major: undefined, type: "bot"}); - assert.deepEqual(botParser.setUA(claudeBot2).getBrowser(), {name: "ClaudeBot", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(claudeWeb).getBrowser(), {name: "Claude-Web", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(bytespider).getBrowser(), {name: "Bytespider", version: undefined, major: undefined, type: "bot"}); - assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8", type: undefined}); - - // try merging Bots & CLIs - const botsAndCLIs = { browser : [...Bots.browser, ...CLIs.browser]}; - const botsAndCLIsParser = new UAParser(botsAndCLIs); - assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); - assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); - - // alternative merge options - const botsAndCLIsParser2 = new UAParser([Bots, CLIs]); - const botsAndCLIsParser3 = new UAParser(facebookBot, [Bots, CLIs]); - assert.deepEqual(botsAndCLIsParser2.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); - assert.deepEqual(botsAndCLIsParser3.getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); - - const emailParser = new UAParser(Emails); - assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); - assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); - - const moduleParser = new UAParser(Modules); - assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"}); - assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); - assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); - }); -}); - -// TODO : move test spec to JSON file - -describe('Testing regexes', () => { - - let regexes; - - before('Read main js file', () => { - let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); - let ast = parseJS(code, { sourceType: 'script' }); - regexes = []; - traverse(ast, { - RegExpLiteral: (path) => { - regexes.push(path.node.pattern); - } - }); - if (regexes.length === 0) { - throw new Error('Regexes cannot be empty!'); - } - }); - - describe('Begin testing', () => { - it('all regexes in extension file', () => { - regexes.forEach(regex => { - describe('Test against `safe-regex` : ' + regex, () => { - it('should be safe from potentially vulnerable regex', () => { - assert.strictEqual(safe(regex), true); - }); +describe('Extensions', () => { + [ + ['CLIs', clis, CLIs], + ['Crawlers', crawlers, Crawlers], + ['Emails', emails, Emails], + ['Fetchers', fetchers, Fetchers], + ['Modules', modules, Modules] + ] + .forEach((list) => { + describe(list[0], () => { + list[1].forEach((agent) => { + it(`Can detect ${agent.desc}`, () => { + let browser = UAParser(agent.ua, list[2]).browser; + assert.strictEqual(String(browser.name), agent.expect.name); + assert.strictEqual(String(browser.version), agent.expect.version); + assert.strictEqual(String(browser.type), agent.expect.type); }); }); }); }); + + const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; + const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; + const axios = 'axios/1.3.5'; + const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; + const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; + + const emailParser = new UAParser(Emails); + assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); + assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); + + const moduleParser = new UAParser(Modules); + assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"}); + assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); + assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); +}); + +describe('Merge', () => { + it('Can merge multiple extensions', () => { + const wget = 'Wget/1.21.1'; + const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; + + // try merging crawlers & CLIs + const crawlersAndCLIs = { browser : [...Crawlers.browser, ...CLIs.browser]}; + const crawlersAndCLIsParser = new UAParser(crawlersAndCLIs); + assert.deepEqual(crawlersAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); + assert.deepEqual(crawlersAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"crawler"}); + + // alternative merge options + const crawlersAndCLIsParser2 = new UAParser([Crawlers, CLIs]); + const crawlersAndCLIsParser3 = new UAParser(facebookBot, [Crawlers, CLIs]); + assert.deepEqual(crawlersAndCLIsParser2.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); + assert.deepEqual(crawlersAndCLIsParser3.getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"crawler"}); + }); +}); + +describe('Testing regexes', () => { + + let regexes; + let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); + let ast = parseJS(code, { sourceType: 'script' }); + regexes = []; + traverse(ast, { + RegExpLiteral: (path) => { + regexes.push(path.node.pattern); + } + }); + + if (regexes.length === 0) { + throw new Error('Regexes cannot be empty!'); + } + + describe('Checking for potentially vulnerable regex', () => { + for (let regex of regexes) { + it('Test against `safe-regex` : ' + regex, () => { + assert.strictEqual(safe(regex), true); + }); + } + }); }); \ No newline at end of file diff --git a/test/specs/browser-clis.json b/test/specs/browser-clis.json new file mode 100644 index 0000000..7d344ff --- /dev/null +++ b/test/specs/browser-clis.json @@ -0,0 +1,42 @@ +[ + { + "desc" : "curl", + "ua" : "curl/7.38.0", + "expect" : + { + "name" : "curl", + "version" : "7.38.0", + "type" : "cli" + } + }, + { + "desc" : "lynx", + "ua" : "Lynx 2.8.8dev.3", + "expect" : + { + "name" : "Lynx", + "version" : "2.8.8dev.3", + "type" : "cli" + } + }, + { + "desc" : "lynx", + "ua" : "Lynx/2.6", + "expect" : + { + "name" : "Lynx", + "version" : "2.6", + "type" : "cli" + } + }, + { + "desc" : "wget", + "ua" : "Wget/1.21.1", + "expect" : + { + "name" : "Wget", + "version" : "1.21.1", + "type" : "cli" + } + } +] diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json new file mode 100644 index 0000000..b72beb2 --- /dev/null +++ b/test/specs/browser-crawlers.json @@ -0,0 +1,92 @@ +[ + { + "desc" : "Applebot", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)", + "expect" : + { + "name" : "Applebot", + "version" : "0.1", + "type" : "crawler" + } + }, + { + "desc" : "Amazonbot", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)", + "expect" : + { + "name" : "Amazonbot", + "version" : "0.1", + "type" : "crawler" + } + }, + { + "desc" : "Bytespider", + "ua" : "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider", + "expect" : + { + "name" : "Bytespider", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "ClaudeBot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)", + "expect" : + { + "name" : "ClaudeBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "ClaudeWeb", + "ua" : "Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)", + "expect" : + { + "name" : "Claude-Web", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "FacebookBot", + "ua" : "Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/", + "expect" : + { + "name" : "FacebookBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot-Video", + "ua" : "Googlebot-Video/1.0", + "expect" : + { + "name" : "Googlebot-Video", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "GPTBot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)", + "expect" : + { + "name" : "GPTBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexBot", + "ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexBot", + "version" : "3.0", + "type" : "crawler" + } + } +] diff --git a/test/specs/browser-emails.json b/test/specs/browser-emails.json new file mode 100644 index 0000000..1ce6b30 --- /dev/null +++ b/test/specs/browser-emails.json @@ -0,0 +1,12 @@ +[ + { + "desc" : "Thunderbird", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0", + "expect" : + { + "name" : "Thunderbird", + "version" : "78.13.0", + "type" : "email" + } + } +] diff --git a/test/specs/browser-fetchers.json b/test/specs/browser-fetchers.json new file mode 100644 index 0000000..646f07b --- /dev/null +++ b/test/specs/browser-fetchers.json @@ -0,0 +1,12 @@ +[ + { + "desc" : "BingPreview", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b", + "expect" : + { + "name" : "BingPreview", + "version" : "1.0b", + "type" : "fetcher" + } + } +] diff --git a/test/specs/browser-modules.json b/test/specs/browser-modules.json new file mode 100644 index 0000000..1639a19 --- /dev/null +++ b/test/specs/browser-modules.json @@ -0,0 +1,12 @@ +[ + { + "desc" : "Scrapy", + "ua" : "Scrapy/1.5.0 (+https://scrapy.org)", + "expect" : + { + "name" : "Scrapy", + "version" : "1.5.0", + "type" : "module" + } + } +] From 5328642e1831e4e7adf6a9dc994e997c63e1bfff Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Jun 2024 01:17:52 +0700 Subject: [PATCH 25/26] Update version to `2.0.0-beta.3` --- CHANGELOG.md | 14 +++ README.md | 15 ++- dist/ua-parser.min.js | 6 +- dist/ua-parser.pack.js | 6 +- package-lock.json | 7 +- package.js | 2 +- package.json | 5 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 23 +++- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 150 +++++++++++++++++++---- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 2 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 6 +- src/main/ua-parser.mjs | 109 +++++++++------- 18 files changed, 254 insertions(+), 103 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3541fe..cc30afa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,20 @@ - Provided Extensions submodule `'ua-parser-js/extensions'` - Provided Helpers submodule `'ua-parser-js/helpers'` +## Version 2.0.0-beta.3 + +- Breaking: + - AR/VR devices moved to new device type: `xr` + - New property in `browser`: `type` + - In `ua-parser-js/extensions` submodule, `bots` divided into `crawler` / `fetcher` +- New features: + - Parse directly from command line using `npx ua-parser-js` + - Extensions can be passed as a list to `UAParser()` +- Add new browser: Pico Browser, Twitter, Wolvic +- Improve browser detection: DuckDuckGo, ICEBrowser, Klar, QQ, Sleipnir +- Improve device detection: Oculus Quest & Oppo Pad +- Update latest client hints spec: `formFactor` -> `formFactors` + ## Version 2.0.0-beta.2 - Increase UA_MAX_LENGTH to 500 diff --git a/README.md b/README.md index e1afb05..116870e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ # UAParser.js -The most comprehensive, compact, & up-to-date JavaScript library to detect +The most comprehensive, compact, & up-to-date isomorphic JavaScript library to detect user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser (client-side) or node.js (server-side). @@ -176,11 +176,11 @@ user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser Price - FREE - FREE - $12 - $25 - $500 + FREE (License) + FREE (License) + $12 (License) + $25 (License) + $500 (License) @@ -205,8 +205,7 @@ see what's new & breaking. ## Contributors -Large or small, your contribution is valuable here. Please read [CONTRIBUTING](CONTRIBUTING.md) -guide first for the instruction details. +Please read [CONTRIBUTING](CONTRIBUTING.md) guide first for the instruction details. diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index f23c2c4..8819c5a 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTOR]){var ff;if(typeof uaCH[FORMFACTOR]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 320b571..5b53896 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;t_?Ti(i,_):i),this}]]).setUA(o),this}Hi.VERSION="2.0.0-beta.2",Hi.BROWSER=e([f,v,p]),Hi.CPU=e([x]),Hi.DEVICE=e([h,g,m,k,y,t,r,o,a]),Hi.ENGINE=Hi.OS=e([f,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Hi),exports.UAParser=Hi):typeof define===c&&define.amd?define(function(){return Hi}):di&&(i.UAParser=Hi);var Ei,Mi=di&&(i.jQuery||i.Zepto);Mi&&!Mi.ua&&(Ei=new Hi,Mi.ua=Ei.getResult(),Mi.ua.get=function(){return Ei.getUA()},Mi.ua.set=function(i){Ei.setUA(i);var e,t=Ei.getResult();for(e in t)Mi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,c){"use strict";function e(i){for(var e={},t=0;t_?Si(i,_):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-beta.3",Ui.BROWSER=e([f,v,p,m]),Ui.CPU=e([x]),Ui.DEVICE=e([h,g,m,k,y,t,r,o,a]),Ui.ENGINE=Ui.OS=e([f,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var Hi,Ei=ui&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(Hi=new Ui,Ei.ua=Hi.getResult(),Ei.ua.get=function(){return Hi.getUA()},Ei.ua.set=function(i){Hi.setUA(i);var e,t=Hi.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 818a3bc..7149841 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "funding": [ { "type": "opencollective", @@ -22,6 +22,9 @@ } ], "license": "AGPL-3.0-or-later", + "bin": { + "ua-parser-js": "script/cli.js" + }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", diff --git a/package.js b/package.js index d58154c..0ff949d 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'faisalman:ua-parser-js', - version: '2.0.0-beta.2', + version: '2.0.0-beta.3', summary: 'Lightweight JavaScript-based user-agent string parser', git: 'https://github.com/faisalman/ua-parser-js.git', documentation: 'readme.md' diff --git a/package.json b/package.json index b4d3362..35fb6d1 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ @@ -18,7 +18,8 @@ "ua-parser-js", "browser-detection", "device-detection", - "os-detection" + "os-detection", + "bot-detection" ], "homepage": "https://github.com/faisalman/ua-parser-js", "contributors": [ diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ea1ce87..e60c482 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.2 +/* Enums for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 7561942..92aa605 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.2 +/* Enums for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -54,7 +54,7 @@ const Browser = Object.freeze({ FENNEC: 'Fennec', FLOCK: 'Flock', FLOW: 'Flow', - GO: 'Go Browser', + GO: 'GoBrowser', GOOGLE_SEARCH: 'GSA', HEYTAP: 'HeyTap', HUAWEI: 'Huawei Browser', @@ -108,6 +108,7 @@ const Browser = Object.freeze({ PALEMOON: 'PaleMoon', PHANTOMJS: 'PhantomJS', PHOENIX: 'Phoenix', + PICOBROWSER: 'Pico Browser', POLARIS: 'Polaris', PUFFIN: 'Puffin', QQ: 'QQBrowser', @@ -132,9 +133,9 @@ const Browser = Object.freeze({ TESLA: 'Tesla', TIKTOK: 'TikTok', TIZEN: 'Tizen Browser', + TWITTER: 'Twitter', UC: 'UCBrowser', UP: 'UP.Browser', - VIERA: 'Viera', VIVALDI: 'Vivaldi', VIVO: 'Vivo Browser', W3M: 'w3m', @@ -143,11 +144,21 @@ const Browser = Object.freeze({ WECHAT: 'WeChat', WEIBO: 'Weibo', WHALE: 'Whale', + WOLVIC: 'Wolvic', YANDEX: 'Yandex' // TODO : test! }); +const BrowserType = Object.freeze({ + CRAWLER: 'crawler', + CLI: 'cli', + EMAIL: 'email', + FETCHER: 'fetcher', + INAPP: 'inapp', + MODULE: 'module' +}); + const CPU = Object.freeze({ ARM : 'arm', ARM_64: 'arm64', @@ -175,7 +186,8 @@ const Device = Object.freeze({ MOBILE: 'mobile', SMARTTV: 'smarttv', TABLET: 'tablet', - WEARABLE: 'wearable' + WEARABLE: 'wearable', + XR: 'xr' }); const Vendor = Object.freeze({ @@ -345,7 +357,8 @@ const OS = Object.freeze({ }); export { - Browser, + Browser, + BrowserType, CPU, Device, Vendor, diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 893f347..5d6f300 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 760f5aa..55a0a0a 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.2 +/* Extensions for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index e0f9c8a..9bec890 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.2 +/* Extensions for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -18,33 +18,74 @@ const VENDOR = 'vendor'; const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; +const CRAWLER = 'crawler'; +const CLI = 'cli'; +const EMAIL = 'email'; +const FETCHER = 'fetcher'; +const INAPP = 'inapp'; +const MODULE = 'module'; -const Apps = Object.freeze({ - browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] - ] -}); - -const Bots = Object.freeze({ - browser : [ - // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], - - // GPTBot - https://platform.openai.com/docs/gptbot - [/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], - - // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] - ] -}); +////////////////////// +// COMMAND LINE APPS +///////////////////// const CLIs = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] + // wget / curl / lynx + [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); +//////////////////////// +// CRAWLERS / SPIDERS +/////////////////////// + +const Crawlers = Object.freeze({ + browser : [ + // Amazonbot - https://developer.amazon.com/amazonbot + // Applebot - http://apple.com/go/applebot + // Bingbot - http://www.bing.com/bingbot.htm + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ + // GPTBot - https://platform.openai.com/docs/gptbot + [/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + [/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Bytespider + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + [/((?:bytespider|(?=yahoo! )slurp))/i], + [NAME, [TYPE, CRAWLER]], + + // ClaudeBot + [/(claude(?:bot|-web))\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Googlebot - http://www.google.com/bot.html + [ + /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Sogou Spider + [/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]] + ] +}); + +////////////////// +// EXTRA DEVICES +///////////////// + const ExtraDevices = Object.freeze({ device : [[ /(nook)[\w ]+build\/(\w+)/i, // Nook @@ -121,13 +162,63 @@ const ExtraDevices = Object.freeze({ ] }); +/////////////// +// EMAIL APPS +////////////// + const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] + // Microsoft Outlook / Thunderbird + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); +/////////////////////// +// ON-DEMAND SCRAPERS +////////////////////// + +const Fetchers = Object.freeze({ + browser : [ + // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot + [/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Google Bots / Snapchat + [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [NAME, [TYPE, FETCHER]], + + + // Slackbot - https://api.slack.com/robots + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // WhatsApp + [/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(yandex(?:sitelinks|userproxy))/i + ], + [NAME, VERSION, [TYPE, FETCHER]] + ] +}); + +//////////////////// +// IN-APP BROWSERS +/////////////////// + +const InApps = Object.freeze({ + browser : [ + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] + ] +}); + +////////////////////// +// MEDIA PLAYER APPS +///////////////////// + const MediaPlayers = Object.freeze({ browser : [[ @@ -234,19 +325,24 @@ const MediaPlayers = Object.freeze({ ] }); +//////////////////////// +// MODULES / LIBRARIES +/////////////////////// + const Modules = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] + // Axios/jsdom/Scrapy + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] ] }); export { - Apps, - Bots, CLIs, + Crawlers, ExtraDevices, Emails, + Fetchers, + InApps, MediaPlayers, Modules }; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 8102361..89f64f0 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 17eea7a..4cbf30e 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-beta.2 +/* Helpers for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index d710c28..cc3533d 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-beta.2 +/* Helpers for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 0c35946..2fa75c7 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-beta.2 +// Type definitions for UAParser.js v2.0.0-beta.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index eda9ba4..a92cb86 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.2', + var LIBVERSION = '2.0.0-beta.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 0f4c2a8..e0e4f4e 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,8 +3,8 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.2', + var LIBVERSION = '2.0.0-beta.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -40,11 +40,12 @@ TABLET = 'tablet', SMARTTV = 'smarttv', WEARABLE = 'wearable', + XR = 'xr', EMBEDDED = 'embedded', USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, BRANDS = 'brands', - FORMFACTOR = 'formFactor', + FORMFACTORS = 'formFactors', FULLVERLIST = 'fullVersionList', PLATFORM = 'platform', PLATFORMVER = 'platformVersion', @@ -53,12 +54,12 @@ CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, - CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', + CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', @@ -102,12 +103,21 @@ // Helper ////////// - var extend = function (regexes, extensions) { - var mergedRegexes = {}; - for (var i in regexes) { - mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; + var extend = function (defaultRgx, extensions) { + var mergedRgx = {}; + var extraRgx = extensions; + if (!isExtensions(extensions)) { + extraRgx = {}; + for (var i in extensions) { + for (var j in extensions[i]) { + extraRgx[j] = extensions[i][j].concat(extraRgx[j] ? extraRgx[j] : []); + } + } } - return mergedRegexes; + for (var k in defaultRgx) { + mergedRgx[k] = extraRgx[k] && extraRgx[k].length % 2 === 0 ? extraRgx[k].concat(defaultRgx[k]) : defaultRgx[k]; + } + return mergedRgx; }, enumerize = function (arr) { var enums = {}; @@ -125,9 +135,9 @@ } return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, - isExtensions = function (obj) { + isExtensions = function (obj, deep) { for (var prop in obj) { - return /^(browser|cpu|device|engine|os)$/.test(prop); + return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false); } }, isString = function (val) { @@ -271,12 +281,13 @@ 'RT' : 'ARM' }, - formFactorMap = { + formFactorsMap = { 'embedded' : 'Automotive', 'mobile' : 'Mobile', 'tablet' : ['Tablet', 'EInk'], 'smarttv' : 'TV', - 'wearable' : ['VR', 'XR', 'Watch'], + 'wearable' : 'Watch', + 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined }; @@ -311,17 +322,20 @@ /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu ], [VERSION, [NAME, 'Baidu']], [ /(kindle)\/([\w\.]+)/i, // Kindle - /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer + /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, + // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir // Trident based /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ + /\bddg\/([\w\.]+)/i // DuckDuckGo + ], [VERSION, [NAME, 'DuckDuckGo']], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser @@ -354,8 +368,10 @@ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ - /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser + /\b(qq)\/([\w\.]+)/i // QQ + ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ + /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon @@ -368,7 +384,7 @@ /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser ], [NAME, VERSION], [ - /(lbbrowser)/i, // LieBao Browser + /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq /\[(linkedin)app\]/i // LinkedIn App for iOS & Android ], [NAME], [ @@ -381,6 +397,7 @@ /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay + /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS @@ -420,23 +437,24 @@ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ + /(wolvic)\/([\w\.]+)/i // Wolvic + ], [NAME, VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality ], [VERSION, [NAME, FIREFOX+' Reality']], [ /ekiohf.+(flow)\/([\w\.]+)/i, // Flow /(swiftfox)/i, // Swiftfox - /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, - // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, + // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix /(firefox)\/([\w\.]+)/i, // Other Firefox-based /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla // Other - /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, - // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i, // Links - /panasonic;(viera)/i // Panasonic Viera - ], [NAME, VERSION], [ + /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser + /(links) \(([\w\.]+)/i // Links + ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] @@ -523,6 +541,8 @@ /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ + /\b(opd2\d{3}a?) bui/i + ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ // Vivo /vivo (\w+)(?: bui|\))/i, @@ -706,12 +726,17 @@ ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ - /droid.+; (glass) \d/i // Google Glass - ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ - /(quest( 2| pro)?)/i // Oculus Quest - ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ + + /////////////////// + // XR + /////////////////// + + /droid.+; (glass) \d/i // Google Glass + ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ + /(quest( \d| pro)?)/i // Oculus Quest + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// // EMBEDDED @@ -842,7 +867,7 @@ var defaultProps = (function () { var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; setProps.call(props.init, [ - [UA_BROWSER, [NAME, VERSION, MAJOR]], + [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_ENGINE, [NAME, VERSION]], @@ -970,7 +995,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], - [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], + [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1090,15 +1115,15 @@ this.set(TYPE, CONSOLE) .set(VENDOR, MICROSOFT); } - if (uaCH[FORMFACTOR]) { + if (uaCH[FORMFACTORS]) { var ff; - if (typeof uaCH[FORMFACTOR] !== 'string') { + if (typeof uaCH[FORMFACTORS] !== 'string') { var idx = 0; - while (!ff && idx < uaCH[FORMFACTOR].length) { - ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); + while (!ff && idx < uaCH[FORMFACTORS].length) { + ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap); } } else { - ff = strMapper(uaCH[FORMFACTOR], formFactorMap); + ff = strMapper(uaCH[FORMFACTORS], formFactorsMap); } this.set(TYPE, ff); } @@ -1149,7 +1174,7 @@ function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { - if (isExtensions(ua)) { + if (isExtensions(ua, true)) { if (typeof extensions === OBJ_TYPE) { headers = extensions; // case UAParser(extensions, headers) } @@ -1159,7 +1184,7 @@ extensions = undefined; } ua = undefined; - } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { + } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { headers = extensions; // case UAParser(ua, headers) extensions = undefined; } @@ -1220,7 +1245,7 @@ } UAParser.VERSION = LIBVERSION; - UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); + UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]); UAParser.CPU = enumerize([ARCHITECTURE]); UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); From 768b62260360c7fb45d5e4c5391ac608d777a38d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 24 Jul 2024 12:47:58 +0700 Subject: [PATCH 26/26] Make it personal - 2.0.0-beta.3-pro-personal --- LICENSE.md | 685 +++--------------------- README.md | 216 +------- dist/ua-parser.min.js | 2 +- dist/ua-parser.pack.js | 2 +- package-lock.json | 8 +- package.json | 6 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 4 +- 12 files changed, 98 insertions(+), 837 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 213ad0b..605947b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,616 +1,73 @@ -# GNU AFFERO GENERAL PUBLIC LICENSE - -Version 3, 19 November 2007 - -Copyright (C) 2007 Free Software Foundation, Inc. - - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - -## Preamble - -The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains -free software for all its users. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - -Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - -A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - -The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - -An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing -under this license. - -The precise terms and conditions for copying, distribution and -modification follow. - -## TERMS AND CONDITIONS - -### 0. Definitions. - -"This License" refers to version 3 of the GNU Affero General Public -License. - -"Copyright" also means copyright-like laws that apply to other kinds -of works, such as semiconductor masks. - -"The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - -To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of -an exact copy. The resulting work is called a "modified version" of -the earlier work or a work "based on" the earlier work. - -A "covered work" means either the unmodified Program or a work based -on the Program. - -To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - -To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user -through a computer network, with no transfer of a copy, is not -conveying. - -An interactive user interface displays "Appropriate Legal Notices" to -the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - -### 1. Source Code. - -The "source code" for a work means the preferred form of the work for -making modifications to it. "Object code" means any non-source form of -a work. - -A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - -The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - -The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can -regenerate automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same -work. - -### 2. Basic Permissions. - -All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, -without conditions so long as your license otherwise remains in force. -You may convey covered works to others for the sole purpose of having -them make modifications exclusively for you, or provide you with -facilities for running those works, provided that you comply with the -terms of this License in conveying all material for which you do not -control copyright. Those thus making or running the covered works for -you must do so exclusively on your behalf, under your direction and -control, on terms that prohibit them from making any copies of your -copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the -conditions stated below. Sublicensing is not allowed; section 10 makes -it unnecessary. - -### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - -No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - -When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such -circumvention is effected by exercising rights under this License with -respect to the covered work, and you disclaim any intention to limit -operation or modification of the work as a means of enforcing, against -the work's users, your or third parties' legal rights to forbid -circumvention of technological measures. - -### 4. Conveying Verbatim Copies. - -You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - -### 5. Conveying Modified Source Versions. - -You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these -conditions: - -- a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. -- b) The work must carry prominent notices stating that it is - released under this License and any conditions added under - section 7. This requirement modifies the requirement in section 4 - to "keep intact all notices". -- c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. -- d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - -A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - -### 6. Conveying Non-Source Forms. - -You may convey a covered work in object code form under the terms of -sections 4 and 5, provided that you also convey the machine-readable -Corresponding Source under the terms of this License, in one of these -ways: - -- a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. -- b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the Corresponding - Source from a network server at no charge. -- c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. -- d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. -- e) Convey the object code using peer-to-peer transmission, - provided you inform other peers where the object code and - Corresponding Source of the work are being offered to the general - public at no charge under subsection 6d. - -A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - -A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, -family, or household purposes, or (2) anything designed or sold for -incorporation into a dwelling. In determining whether a product is a -consumer product, doubtful cases shall be resolved in favor of -coverage. For a particular product received by a particular user, -"normally used" refers to a typical or common use of that class of -product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected -to use, the product. A product is a consumer product regardless of -whether the product has substantial commercial, industrial or -non-consumer uses, unless such uses represent the only significant -mode of use of the product. - -"Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to -install and execute modified versions of a covered work in that User -Product from a modified version of its Corresponding Source. The -information must suffice to ensure that the continued functioning of -the modified object code is in no case prevented or interfered with -solely because modification has been made. - -If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - -The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or -updates for a work that has been modified or installed by the -recipient, or for the User Product in which it has been modified or -installed. Access to a network may be denied when the modification -itself materially and adversely affects the operation of the network -or violates the rules and protocols for communication across the -network. - -Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - -### 7. Additional Terms. - -"Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders -of that material) supplement the terms of this License with terms: - -- a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or -- b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or -- c) Prohibiting misrepresentation of the origin of that material, - or requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or -- d) Limiting the use for publicity purposes of names of licensors - or authors of the material; or -- e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or -- f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions - of it) with contractual assumptions of liability to the recipient, - for any liability that these contractual assumptions directly - impose on those licensors and authors. - -All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; the -above requirements apply either way. - -### 8. Termination. - -You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - -### 9. Acceptance Not Required for Having Copies. - -You are not required to accept this License in order to receive or run -a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - -### 10. Automatic Licensing of Downstream Recipients. - -Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - -An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - -### 11. Patents. - -A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - -A contributor's "essential patent claims" are all patent claims owned -or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - -In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - -If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - -A patent license is "discriminatory" if it does not include within the -scope of its coverage, prohibits the exercise of, or is conditioned on -the non-exercise of one or more of the rights that are specifically -granted under this License. You may not convey a covered work if you -are a party to an arrangement with a third party that is in the -business of distributing software, under which you make payment to the -third party based on the extent of your activity of conveying the -work, and under which the third party grants, to any of the parties -who would receive the covered work from you, a discriminatory patent -license (a) in connection with copies of the covered work conveyed by -you (or copies made from those copies), or (b) primarily for and in -connection with specific products or compilations that contain the -covered work, unless you entered into that arrangement, or that patent -license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - -### 12. No Surrender of Others' Freedom. - -If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under -this License and any other pertinent obligations, then as a -consequence you may not convey it at all. For example, if you agree to -terms that obligate you to collect a royalty for further conveying -from those to whom you convey the Program, the only way you could -satisfy both those terms and this License would be to refrain entirely -from conveying the Program. - -### 13. Remote Network Interaction; Use with the GNU General Public License. - -Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your -version supports such interaction) an opportunity to receive the -Corresponding Source of your version by providing access to the -Corresponding Source from a network server at no charge, through some -standard or customary means of facilitating copying of software. This -Corresponding Source shall include the Corresponding Source for any -work covered by version 3 of the GNU General Public License that is -incorporated pursuant to the following paragraph. - -Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - -### 14. Revised Versions of this License. - -The Free Software Foundation may publish revised and/or new versions -of the GNU Affero General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever -published by the Free Software Foundation. - -If the Program specifies that a proxy can decide which future versions -of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - -Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - -### 15. Disclaimer of Warranty. - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT -WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND -PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR -CORRECTION. - -### 16. Limitation of Liability. - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR -CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT -NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR -LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM -TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER -PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -### 17. Interpretation of Sections 15 and 16. - -If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. +# UAPARSER.JS PRO PERSONAL LICENSE + +Version 2, July 2024 + +Copyright (C) 2023-2024 Faisal Salman + +--- + +## Definitions + +"We" are the team behind UAParser.js. + +"You" are the individual who is responsible for purchasing this license. + +"The Code" is UAParser.js. + +"Project" is what you built with The Code. + +--- + +## License + +We retain all title, intellectual property, and ownership rights to The Code. + +The Code is licensed, not sold, to You for use solely subject to the terms and conditions detailed here. + +We grant You (and only You) a personal, limited, non-exclusive, non-transferable, non-sublicensable, royalty-free right to use, copy, and modify The Code. + +This license is only valid for You as one (1) individual and cannot be transferred to another individual or organization. + +--- + +## Rights + +You may use and modify The Code to create as many personal, hobby, educational, and other non-profit projects as you want for yourself. + +You may create any number of copies of The Code for yourself. + +You have the right to get lifetime updates and one (1) year of support, starting from the time you make the purchase. + +--- + +## Restrictions + +You may not use or modify The Code in such a way that it may be used directly for commercial purposes. + +You may not redistribute The Code, as-is or modified, except as a part of a Project that you made for yourself. + +You may not deliver a Project that contains The Code as an open-source Project that might be used for commercial purposes by the general public, except with our written consent. + +You may not use The Code for unlawful, inappropriate, illegal, or offensive purposes. + +--- + +## Limitations + +The Code is provided 'as is' without warranty of any kind, expressed or implied. + +We shall not be liable for any damages, including but not limited to, direct, indirect, special, incidental, or consequential damages or losses that occur by the use of The Code. + +We reserve the right to discontinue the lifetime updates for The Code at any time, with or without notice. + +We offer support only for questions within the scope of The Code's functionality or related issues at our sole discretion. + +--- + +## Termination + +This license is effective indefinitely but can be revoked at any time if there is a violation of any of the terms. + +--- END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/README.md b/README.md index 116870e..a1dde43 100644 --- a/README.md +++ b/README.md @@ -1,219 +1,23 @@

- -

- -

- - - - - - - - +

# UAParser.js -The most comprehensive, compact, & up-to-date isomorphic JavaScript library to detect -user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser -(client-side) or node.js (server-side). +Thank you for purchasing UAParser.js PRO Personal License, if you haven't please oreder here: https://store.faisalman.com -## License Options +# Download - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Open-Source EditionsPRO / Commercial Editions
License optionsMITAGPLPRO PersonalPRO BusinessPRO Enterprise
Browser detection⚠️
CPU detection⚠️
Device detection⚠️
Engine detection⚠️
OS detection⚠️
Enhanced detection⛔️
Client Hints support⛔️
Extras (Apps, Bots, Libs, Emails, Media Players, etc)⛔️
CommonJS support
ES modules support⛔️
npm module available
TypeScript declarations available⚠️
Allowed for commercial use⛔️
Permissive (non-copyleft) license⛔️
Unlimited use per 1 license⚠️
1-year support⛔️⛔️
Lifetime updates
PriceFREE (License)FREE (License)$12 (License)$25 (License)$500 (License)
- GET THE PRO PACKAGES 📥 -
- -## Version 2.0 -Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to -see what's new & breaking. +```sh +npm install @ua-parser-js/pro-personal +``` # Documentation - * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation - * v2.0: https://docs.uaparser.js.org/v2 +https://docs.uaparser.dev -# Development +# License -## Contributors +UAParser.js PRO Personal -Please read [CONTRIBUTING](CONTRIBUTING.md) guide first for the instruction details. - - - - - -Made with [contributors-img](https://contrib.rocks). - -## Backers & Sponsors - - - +Copyright (c) 2012-2024 Faisal Salman <> diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 8819c5a..c4f6d6a 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-beta.3 Copyright © 2012-2024 Faisal Salman - AGPLv3 License */ + UAParser.js PRO Personal License */ (function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 5b53896..2fe5806 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-beta.3 Copyright © 2012-2024 Faisal Salman - AGPLv3 License */ + UAParser.js PRO Personal License */ !function(i,c){"use strict";function e(i){for(var e={},t=0;t_?Si(i,_):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-beta.3",Ui.BROWSER=e([f,v,p,m]),Ui.CPU=e([x]),Ui.DEVICE=e([h,g,m,k,y,t,r,o,a]),Ui.ENGINE=Ui.OS=e([f,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var Hi,Ei=ui&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(Hi=new Ui,Ei.ua=Hi.getResult(),Ei.ua.get=function(){return Hi.getUA()},Ei.ua.set=function(i){Hi.setUA(i);var e,t=Hi.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7149841..7a1c6f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "ua-parser-js", + "name": "@ua-parser-js/pro-personal", "version": "2.0.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "ua-parser-js", + "name": "@ua-parser-js/pro-personal", "version": "2.0.0-beta.3", "funding": [ { @@ -21,9 +21,9 @@ "url": "https://github.com/sponsors/faisalman" } ], - "license": "AGPL-3.0-or-later", + "license": "UAParser.js-PRO-Personal", "bin": { - "ua-parser-js": "script/cli.js" + "pro-personal": "script/cli.js" }, "devDependencies": { "@babel/parser": "7.15.8", diff --git a/package.json b/package.json index 35fb6d1..0665ddb 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "title": "UAParser.js", - "name": "ua-parser-js", + "title": "UAParser.js PRO Personal", + "name": "@ua-parser-js/pro-personal", "version": "2.0.0-beta.3", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", @@ -226,7 +226,7 @@ "type": "git", "url": "https://github.com/faisalman/ua-parser-js.git" }, - "license": "AGPL-3.0-or-later", + "license": "UAParser.js-PRO-Personal", "engines": { "node": "*" }, diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index e60c482..f10d882 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -2,7 +2,7 @@ /* Enums for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman - AGPLv3 License */ + UAParser.js PRO Personal License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 92aa605..9c18886 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -6,7 +6,7 @@ /* Enums for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman - AGPLv3 License */ + UAParser.js PRO Personal License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 55a0a0a..0ba1bf3 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -2,7 +2,7 @@ /* Extensions for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman - AGPLv3 License */ + UAParser.js PRO Personal License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 9bec890..572a55d 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -6,7 +6,7 @@ /* Extensions for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman - AGPLv3 License */ + UAParser.js PRO Personal License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a92cb86..589d5a4 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,10 +1,10 @@ ///////////////////////////////////////////////////////////////////////////////// /* UAParser.js v2.0.0-beta.3 Copyright © 2012-2024 Faisal Salman - AGPLv3 License *//* + UAParser.js PRO Personal License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. - Demo : https://faisalman.github.io/ua-parser-js + Demo : https://uaparser.dev Source : https://github.com/faisalman/ua-parser-js */ ///////////////////////////////////////////////////////////////////////////////// diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index e0e4f4e..53cddcb 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -5,10 +5,10 @@ ///////////////////////////////////////////////////////////////////////////////// /* UAParser.js v2.0.0-beta.3 Copyright © 2012-2024 Faisal Salman - AGPLv3 License *//* + UAParser.js PRO Personal License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. - Demo : https://faisalman.github.io/ua-parser-js + Demo : https://uaparser.dev Source : https://github.com/faisalman/ua-parser-js */ /////////////////////////////////////////////////////////////////////////////////