From 7fbb93c99acf1f8caf2164cabbf85e2b054a7d6d Mon Sep 17 00:00:00 2001
From: shaharmor
Date: Sun, 2 Jul 2017 16:14:23 +0300
Subject: [PATCH 001/388] Detect "Chrome Mobile" & "Firefox Mobile"
---
src/ua-parser.js | 16 ++++++++++++----
test/browser-test.json | 34 ++++++++++++++++++++++++++++++++--
2 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 24292f2ab..b3a8c6d26 100644
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -293,7 +293,7 @@
], [VERSION, [NAME, 'Chrome Headless']], [
/\swv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
- ], [[NAME, /(.+)/, '$1 WebView'], VERSION], [
+ ], [[NAME, 'Chrome WebView'], VERSION], [
/((?:oculus|samsung)browser)\/([\w\.]+)/i
], [[NAME, /(.+(?:g|us))(.+)/, '$1 $2'], VERSION], [ // Oculus / Samsung Browser
@@ -301,8 +301,13 @@
/android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)*/i // Android Browser
], [VERSION, [NAME, 'Android Browser']], [
- /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i
- // Chrome/OmniWeb/Arora/Tizen/Nokia
+ /(omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i // OmniWeb/Arora/Tizen/Nokia
+ ], [NAME, VERSION], [
+
+ /(chrome)\/([\w\.]+) Mobile/i // Chrome Mobile
+ ], [[NAME, 'Chrome Mobile'], VERSION], [
+
+ /(chrome)\/v?([\w\.]+)/i // Chrome
], [NAME, VERSION], [
/(dolfin)\/([\w\.]+)/i // Dolphin
@@ -314,8 +319,11 @@
/(coast)\/([\w\.]+)/i // Opera Coast
], [[NAME, 'Opera Coast'], VERSION], [
+ /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile
+ ], [[NAME, 'Firefox Mobile'], VERSION], [
+
/fxios\/([\w\.-]+)/i // Firefox for iOS
- ], [VERSION, [NAME, 'Firefox']], [
+ ], [VERSION, [NAME, 'Firefox Mobile']], [
/version\/([\w\.]+).+?mobile\/\w+\s(safari)/i // Mobile Safari
], [VERSION, [NAME, 'Mobile Safari']], [
diff --git a/test/browser-test.json b/test/browser-test.json
index d99e3deb1..e39464bfb 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -934,9 +934,39 @@
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) FxiOS/1.1 Mobile/13B143 Safari/601.1.46",
"expect" :
{
- "name" : "Firefox",
+ "name" : "Firefox Mobile",
"version" : "1.1",
"major" : "1"
}
+ },
+ {
+ "desc" : "Chrome Mobile",
+ "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
+ "expect" :
+ {
+ "name" : "Chrome Mobile",
+ "version" : "58.0.3029.83",
+ "major" : "58"
+ }
+ },
+ {
+ "desc" : "Firefox Mobile",
+ "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) FxiOS/7.5b3349 Mobile/14F89 Safari/603.2.4",
+ "expect" :
+ {
+ "name" : "Firefox Mobile",
+ "version" : "7.5b3349",
+ "major" : "7"
+ }
+ },
+ {
+ "desc" : "Firefox Mobile",
+ "ua" : "Mozilla/5.0 (Android 5.0; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0",
+ "expect" :
+ {
+ "name" : "Firefox Mobile",
+ "version" : "41.0",
+ "major" : "41"
+ }
}
-]
+]
\ No newline at end of file
From 825edbbf91276e5e0843be15b592da090fbc43d4 Mon Sep 17 00:00:00 2001
From: liujunlve
Date: Fri, 30 Jul 2021 15:32:56 +0800
Subject: [PATCH 002/388] Fix #509 add support for Internet Explorer 8
---
src/ua-parser.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 2a6236707..3581c9a33 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -114,6 +114,7 @@
// try matching uastring with regexes
while (j < regex.length && !matches) {
+ if (!regex[j]) { break; }
matches = regex[j++].exec(ua);
if (!!matches) {
From 62cc99d7cf56254bf0b131d00136020ba4b4d3f9 Mon Sep 17 00:00:00 2001
From: Paris Morgan
Date: Tue, 21 Sep 2021 12:23:49 -0700
Subject: [PATCH 003/388] Samsung Galaxy S20 5G
---
src/ua-parser.js | 2 +-
test/device-test.json | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 7d8beb671..2f6711270 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -371,7 +371,7 @@
// Samsung
/\b(sch-i[89]0\d|shw-m380s|sm-[pt]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i
], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [
- /\b((?:s[cgp]h|gt|sm)-\w+|galaxy nexus)/i,
+ /\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,
/samsung[- ]([-\w]+)/i,
/sec-(sgh\w+)/i
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
diff --git a/test/device-test.json b/test/device-test.json
index ae9392fe8..4311a78f8 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1404,6 +1404,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Samsung Galaxy S20 5G",
+ "ua": "Mozilla/5.0 (Linux; Android 10; SCG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Samsung",
+ "model": "SCG01",
+ "type": "mobile"
+ }
+ },
{
"desc": "Samsung Galaxy Note 10+",
"ua": "Mozilla/5.0 (Linux; Android 9; SM-N976V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36",
From 779b5dc1e578cff44c96c60678f8449bd9018fd7 Mon Sep 17 00:00:00 2001
From: KnifeLemon
Date: Tue, 12 Apr 2022 12:57:28 +0900
Subject: [PATCH 004/388] add KakaoTalk App, KakaoStory App regex
---
src/ua-parser.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 55c3c8d20..e34036772 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -57,7 +57,8 @@
SONY = 'Sony',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
- FACEBOOK = 'Facebook';
+ FACEBOOK = 'Facebook',
+ KAKAO = 'Kakao';
///////////
// Helper
@@ -287,6 +288,11 @@
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
+ / wv\).+(chrome)\/([\w\.]+).+(kakaotalk)/i // KakaoTalk App
+ ], [[NAME, KAKAO+'Talk'], VERSION], [
+ / wv\).+(chrome)\/([\w\.]+).+(kakaostory)/i // KakaoStory App
+ ], [[NAME, KAKAO+'Story'], VERSION], [
+
/headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
], [VERSION, [NAME, CHROME+' Headless']], [
From 0b423078c8966442fdb617b72a2e0195d5aefb40 Mon Sep 17 00:00:00 2001
From: KnifeLemon
Date: Tue, 12 Apr 2022 14:17:48 +0900
Subject: [PATCH 005/388] Add Kakao App, Naver App
---
src/ua-parser.js | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index e34036772..3127a9b28 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -58,7 +58,8 @@
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
FACEBOOK = 'Facebook',
- KAKAO = 'Kakao';
+ KAKAO = 'Kakao',
+ NAVER = 'Naver';
///////////
// Helper
@@ -223,6 +224,9 @@
/\bopr\/([\w\.]+)/i // Opera Webkit
], [VERSION, [NAME, OPERA]], [
+ /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp
+ ], [VERSION,[NAME, /(.+);\s+(.+);\s+(.+);\s+(.+)/i, NAVER+' $1 $2']], [
+
// Mixed
/(kindle)\/([\w\.]+)/i, // Kindle
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
@@ -288,10 +292,8 @@
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
- / wv\).+(chrome)\/([\w\.]+).+(kakaotalk)/i // KakaoTalk App
- ], [[NAME, KAKAO+'Talk'], VERSION], [
- / wv\).+(chrome)\/([\w\.]+).+(kakaostory)/i // KakaoStory App
- ], [[NAME, KAKAO+'Story'], VERSION], [
+ / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App
+ ], [VERSION, [NAME, /(.+)/i, KAKAO+'$1']], [
/headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
], [VERSION, [NAME, CHROME+' Headless']], [
From 534ba7dd7c7648ac27447b0de7bfad33e733bf4e Mon Sep 17 00:00:00 2001
From: KnifeLemon
Date: Tue, 12 Apr 2022 16:21:31 +0900
Subject: [PATCH 006/388] Add iOS Naver, Kakao regex
---
src/ua-parser.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 3127a9b28..cdf88eac2 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -224,8 +224,9 @@
/\bopr\/([\w\.]+)/i // Opera Webkit
], [VERSION, [NAME, OPERA]], [
- /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp
- ], [VERSION,[NAME, /(.+);\s+(.+);\s+(.+);\s+(.+)/i, NAVER+' $1 $2']], [
+ /Safari\/([-\w\.]+).+NAVER\((.*)\)/i, // Naver InApp for iOS
+ /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp Android
+ ], [VERSION,[NAME, /(.+); (.+); [0-9]+;.+/i, NAVER+' $1 $2']], [
// Mixed
/(kindle)\/([\w\.]+)/i, // Kindle
@@ -292,7 +293,8 @@
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
- / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App
+ /AppleWebKit\/([\w\.]+).+kakao(.*)\s+/i, // Kakao App for iOS
+ / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App for Android
], [VERSION, [NAME, /(.+)/i, KAKAO+'$1']], [
/headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
From 6fde0d92d60752821f214394f337259589b3187e Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Tue, 8 Nov 2022 15:08:50 +0100
Subject: [PATCH 007/388] Support Panasonic Viera Smart TVs
---
src/ua-parser.js | 68 ++++++++++++++++++++++++------------------
test/browser-test.json | 10 +++++++
test/device-test.json | 9 ++++++
test/os-test.json | 9 ++++++
4 files changed, 67 insertions(+), 29 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index b47ad2364..5faa66c67 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -53,9 +53,11 @@
MICROSOFT = 'Microsoft',
MOTOROLA = 'Motorola',
OPERA = 'Opera',
+ PANASONIC = 'Panasonic',
SAMSUNG = 'Samsung',
SHARP = 'Sharp',
SONY = 'Sony',
+ VIERA = 'Viera',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
FACEBOOK = 'Facebook';
@@ -295,6 +297,8 @@
/ wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
], [[NAME, CHROME+' WebView'], VERSION], [
+ /Panasonic;(VIERA)/i // Panasonic Viera
+ ], [[NAME, VIERA]], [
/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
], [VERSION, [NAME, 'Android '+BROWSER]], [
@@ -367,6 +371,35 @@
device : [[
+ ///////////////////
+ // SMARTTVS
+ ///////////////////
+
+ /smart-tv.+(samsung)/i // Samsung
+ ], [VENDOR, [TYPE, SMARTTV]], [
+ /hbbtv.+maple;(\d+)/i
+ ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
+ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV
+ ], [[VENDOR, LG], [TYPE, SMARTTV]], [
+ /(apple) ?tv/i // Apple TV
+ ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
+ /crkey/i // Google Chromecast
+ ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
+ /droid.+aft(\w)( bui|\))/i // Fire TV
+ ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
+ /\(dtv[\);].+(aquos)/i,
+ /(aquos-tv[\w ]+)\)/i // Sharp
+ ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[
+ /(bravia[\w ]+)( bui|\))/i // Sony
+ ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
+ /(mitv-\w{5}) bui/i // Xiaomi
+ ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
+ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
+ /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices
+ ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
+ /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
+ ], [[TYPE, SMARTTV]], [
+
//////////////////////////
// MOBILES & TABLETS
// Ordered by popularity
@@ -601,35 +634,6 @@
/\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox
], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
- ///////////////////
- // SMARTTVS
- ///////////////////
-
- /smart-tv.+(samsung)/i // Samsung
- ], [VENDOR, [TYPE, SMARTTV]], [
- /hbbtv.+maple;(\d+)/i
- ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
- /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV
- ], [[VENDOR, LG], [TYPE, SMARTTV]], [
- /(apple) ?tv/i // Apple TV
- ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
- /crkey/i // Google Chromecast
- ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
- /droid.+aft(\w)( bui|\))/i // Fire TV
- ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
- /\(dtv[\);].+(aquos)/i,
- /(aquos-tv[\w ]+)\)/i // Sharp
- ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[
- /(bravia[\w ]+)( bui|\))/i // Sony
- ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
- /(mitv-\w{5}) bui/i // Xiaomi
- ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
- /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
- /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices
- ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
- /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
- ], [[TYPE, SMARTTV]], [
-
///////////////////
// WEARABLES
///////////////////
@@ -729,10 +733,16 @@
/(cros) [\w]+ ([\w\.]+\w)/i // Chromium OS
], [[NAME, 'Chromium OS'], VERSION],[
+ // Smart TVs
+ /Panasonic;VIERA/i // Panasonic Viera
+ ], [[NAME, VIERA]], [
+
+
// Console
/(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation
/(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
+
// Other
/\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm
/(mint)[\/\(\) ]?(\w*)/i, // Mint
diff --git a/test/browser-test.json b/test/browser-test.json
index bf8163a1f..4db45f058 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -1278,6 +1278,16 @@
"major" : "1"
}
},
+ {
+ "desc" : "Viera",
+ "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)",
+ "expect" :
+ {
+ "name" : "Viera",
+ "version" : "undefined",
+ "major" : "undefined"
+ }
+ },
{
"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",
diff --git a/test/device-test.json b/test/device-test.json
index bad1e50af..235b0e605 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1431,6 +1431,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Panasonic TX-32CSW514 SmartTV",
+ "ua": "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)",
+ "expect": {
+ "vendor": "Panasonic",
+ "model": "VIERA 2015",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Philips SmartTV",
"ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60",
diff --git a/test/os-test.json b/test/os-test.json
index 57a4d8a95..b7b78fec5 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -1043,6 +1043,15 @@
"version" : "undefined"
}
},
+ {
+ "desc" : "Panasonic Viera",
+ "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)",
+ "expect" :
+ {
+ "name" : "Viera",
+ "version" : "undefined"
+ }
+ },
{
"desc" : "HP-UX",
"ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0",
From 5fd47ee00d4aa01838da870b6fcf965812622291 Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Tue, 8 Nov 2022 15:26:12 +0100
Subject: [PATCH 008/388] Support Panasonic 2018+ smart tvs
---
src/ua-parser.js | 2 +-
test/device-test.json | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 5faa66c67..f825fafff 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -395,7 +395,7 @@
/(mitv-\w{5}) bui/i // Xiaomi
], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
/\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
- /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices
+ /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices
], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
/\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
], [[TYPE, SMARTTV]], [
diff --git a/test/device-test.json b/test/device-test.json
index 235b0e605..d6525f20a 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1440,6 +1440,15 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Panasonic TX-40FXW724 SmartTV",
+ "ua": "HbbTV/1.4.1 (+DRM;Panasonic;SmartTV2018mid;3.024;4301-0003 0002-0000;SmartTV2018;)",
+ "expect": {
+ "vendor": "Panasonic",
+ "model": "SmartTV2018mid",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Philips SmartTV",
"ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60",
From 98096f14548d12befbb49555447aae953b65d074 Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Tue, 8 Nov 2022 15:28:14 +0100
Subject: [PATCH 009/388] Add test for panasonic 2020 smart tv
---
test/device-test.json | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/test/device-test.json b/test/device-test.json
index d6525f20a..154b20e91 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1449,6 +1449,15 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Panasonic TX-43HXW904 SmartTV",
+ "ua": "HbbTV/1.5.1 (+DRM;Panasonic;SmartTV2020mid;3.326;4301-0003 0008-0000;com.panasonic.SmartTV2020mid;)",
+ "expect": {
+ "vendor": "Panasonic",
+ "model": "SmartTV2020mid",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Philips SmartTV",
"ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60",
From 8ffdf232f488a6f59733241ba177538882c8189f Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Tue, 8 Nov 2022 15:30:54 +0100
Subject: [PATCH 010/388] Add test for panasonic SAT receiver
---
test/device-test.json | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/test/device-test.json b/test/device-test.json
index 154b20e91..69323c332 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1458,6 +1458,15 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Panasonic DMR-HST130 SAT receiver",
+ "ua": "HbbTV/1.1.1 (+PVR;Panasonic;DIGA WebKit M8658;3.420;;)",
+ "expect": {
+ "vendor": "Panasonic",
+ "model": "DIGA WebKit M8658",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Philips SmartTV",
"ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60",
From db5567d61487a9940aeceb25eba68bacc1286df4 Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Tue, 8 Nov 2022 15:56:40 +0100
Subject: [PATCH 011/388] Support Loewe Smart TVs
---
src/ua-parser.js | 5 ++++-
test/device-test.json | 9 +++++++++
test/os-test.json | 9 +++++++++
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index f825fafff..c9d8655bf 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -52,6 +52,7 @@
LG = 'LG',
MICROSOFT = 'Microsoft',
MOTOROLA = 'Motorola',
+ NETRANGE= 'Netrange',
OPERA = 'Opera',
PANASONIC = 'Panasonic',
SAMSUNG = 'Samsung',
@@ -734,8 +735,10 @@
], [[NAME, 'Chromium OS'], VERSION],[
// Smart TVs
- /Panasonic;VIERA/i // Panasonic Viera
+ /Panasonic;VIERA/i // Panasonic Viera
], [[NAME, VIERA]], [
+ /NETRANGEMMH/i // Netrange
+ ], [[NAME, NETRANGE]], [
// Console
diff --git a/test/device-test.json b/test/device-test.json
index 69323c332..f4e4c5b03 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -970,6 +970,15 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Loewe Smart TV",
+ "ua": "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH",
+ "expect": {
+ "vendor": "LOEWE",
+ "model": "SL410",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Meizu M5 Note",
"ua": "Mozilla/5.0 (Linux; Android 6.0; M5 Note Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043024 Safari/537.36 MicroMessenger/6.5.7.1040 NetType/WIFI Language/zh_CN",
diff --git a/test/os-test.json b/test/os-test.json
index b7b78fec5..006504cbf 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -1052,6 +1052,15 @@
"version" : "undefined"
}
},
+ {
+ "desc" : "Netrange Smart TV",
+ "ua" : "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH",
+ "expect" :
+ {
+ "name" : "Netrange",
+ "version" : "undefined"
+ }
+ },
{
"desc" : "HP-UX",
"ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0",
From a8a4e71345b22f7ba0fce024426d046a5da3183c Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Tue, 8 Nov 2022 16:17:57 +0100
Subject: [PATCH 012/388] Add tests for JVC smart tvs
---
test/device-test.json | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/test/device-test.json b/test/device-test.json
index f4e4c5b03..dd2111a49 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -790,6 +790,24 @@
"type": "mobile"
}
},
+ {
+ "desc": "JVC LT-43V55LFA Smart TV",
+ "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB211 VSTVB MB200 HbbTV/1.2.1 (; JVC; MB211; 3.19.4.2; _TV_NT72563_2017 SmartTvA/3.0.0",
+ "expect": {
+ "vendor": "JVC",
+ "model": "MB211",
+ "type": "smarttv"
+ }
+ },
+ {
+ "desc": "JVC LT-43V65LUA Smart TV",
+ "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB130 VSTVB MB100 HbbTV/1.2.1 (; JVC; MB130; 5.7.20.0; _TV_G10_2017;) SmartTvA/3.0.0",
+ "expect": {
+ "vendor": "JVC",
+ "model": "MB130",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Lenovo Tab 2",
"ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36",
From 528e7aea1cd1a300043e00f0a7299edc348e3afc Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Wed, 9 Nov 2022 11:03:13 +0100
Subject: [PATCH 013/388] Support philips smart tvs
---
src/ua-parser.js | 3 +++
test/device-test.json | 27 +++++++++++++++++++++++++++
test/os-test.json | 9 +++++++++
3 files changed, 39 insertions(+)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index c9d8655bf..d8068a0b2 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -53,6 +53,7 @@
MICROSOFT = 'Microsoft',
MOTOROLA = 'Motorola',
NETRANGE= 'Netrange',
+ NETTV = 'NetTV',
OPERA = 'Opera',
PANASONIC = 'Panasonic',
SAMSUNG = 'Samsung',
@@ -739,6 +740,8 @@
], [[NAME, VIERA]], [
/NETRANGEMMH/i // Netrange
], [[NAME, NETRANGE]], [
+ /nettv\/(\d\.\d.\d)/i // NetTV
+ ], [VERSION, [NAME, NETTV]], [
// Console
diff --git a/test/device-test.json b/test/device-test.json
index dd2111a49..136b61e0f 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1503,6 +1503,33 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Philips 32PFL6606K/02 SmartTV (2011)",
+ "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70",
+ "expect": {
+ "vendor": "Philips",
+ "model": "",
+ "type": "smarttv"
+ }
+ },
+ {
+ "desc": "Philips 32PFL6606K/02 SmartTV (2013)",
+ "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70",
+ "expect": {
+ "vendor": "Philips",
+ "model": "",
+ "type": "smarttv"
+ }
+ },
+ {
+ "desc": "Philips 32PHS5301/12 SmartTV (2016)",
+ "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36 OPR/29.0.1803.0 OMI/4.5.23.37.MOT2.13 HbbTV/1.2.1 (;Philips;32PHS5301/12;;_TV_MT5800;) Firmware/TPM161E_012.002.045.001 en",
+ "expect": {
+ "vendor": "Philips",
+ "model": "32PHS5301/12",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Roku",
"ua": "Mozilla/5.0 (Roku) AppleWebKit/537.36 (KHTML, like Gecko) Web/1.1 Safari/537.36",
diff --git a/test/os-test.json b/test/os-test.json
index 006504cbf..e5b13c27f 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -1061,6 +1061,15 @@
"version" : "undefined"
}
},
+ {
+ "desc" : "NetTV 3.2.1",
+ "ua" : "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70",
+ "expect" :
+ {
+ "name" : "NetTV",
+ "version" : "3.2.1"
+ }
+ },
{
"desc" : "HP-UX",
"ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0",
From e5942dc505ac219872e0da384dd8e4fbe6a738b6 Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Thu, 10 Nov 2022 09:07:08 +0100
Subject: [PATCH 014/388] Add samsung testcases
---
test/device-test.json | 9 +++++++++
test/os-test.json | 9 +++++++++
2 files changed, 18 insertions(+)
diff --git a/test/device-test.json b/test/device-test.json
index 136b61e0f..fbdc9f4bf 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1809,6 +1809,15 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Samsung SmartTV HBBTV",
+ "ua": "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17",
+ "expect": {
+ "vendor": "Samsung",
+ "model": "SmartTV2021:UAU7000",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Sharp AQUOS-TVX19B",
"ua": "Mozilla/5.0 (Linux; Android 9; AQUOS-TVX19B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36",
diff --git a/test/os-test.json b/test/os-test.json
index e5b13c27f..3408ddd9d 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -197,6 +197,15 @@
"version" : "2.3"
}
},
+ {
+ "desc" : "Tizen 6.0",
+ "ua" : "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17",
+ "expect" :
+ {
+ "name" : "Tizen",
+ "version" : "6.0"
+ }
+ },
{
"desc" : "Android",
"ua" : "Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; VM670 Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko)",
From df7551c9b69d067cb2a14c91849f0c8715c0af86 Mon Sep 17 00:00:00 2001
From: Garrit Franke
Date: Thu, 10 Nov 2022 11:20:31 +0100
Subject: [PATCH 015/388] Add support for TechniSAT TVs and SATs
---
src/ua-parser.js | 2 ++
test/device-test.json | 20 ++++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index d8068a0b2..d3895e064 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -396,6 +396,8 @@
], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
/(mitv-\w{5}) bui/i // Xiaomi
], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
+ /Hbbtv.*(technisat) (.*);/i // TechniSAT
+ ], [VENDOR, MODEL, [TYPE, SMARTTV]], [
/\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
/hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices
], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
diff --git a/test/device-test.json b/test/device-test.json
index fbdc9f4bf..fbcc5441f 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -2045,6 +2045,26 @@
"type": "embedded"
}
},
+ {
+ "desc" : "TechniSAT Digit ISIO S SAT receiver",
+ "ua" : "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50",
+ "expect" :
+ {
+ "vendor": "TechniSat",
+ "model": "Digit ISIO S",
+ "type": "smarttv"
+ }
+ },
+ {
+ "desc" : "TechniSAT MultyVision SmartTV",
+ "ua" : "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50",
+ "expect" :
+ {
+ "vendor": "TechniSat",
+ "model": "MultyVision ISIO",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Xiaomi 2013023",
"ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; en-US; 2013023 Build/HM2013023) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/10.0.1.512 U3/0.8.0 Mobile Safari/533.1",
From cf093222e5a2dbdff1855539144e457c9c47a9d1 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 24 Jan 2023 00:42:40 +0700
Subject: [PATCH 016/388] Fix #557 #612 #629 - Update the changelog
---
changelog.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)
diff --git a/changelog.md b/changelog.md
index 05fc6b90b..addaff4d3 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,49 @@
# UAParser.js Changelog
-## Version 0.8.0
\ No newline at end of file
+# Version 0.7 / 1.0
+
+Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion.
+
+## Version 0.7.30 / 1.0.1
+
+- Add new browser : Obigo, UP.Browser, Klar
+- Add new device : Oculus, Roku
+- Add new OS: Maemo, HP-UX, Android-x86, Deepin, elementary OS, GhostBSD, Linspire, Manjaro, Sabayon
+- Improve detection for Sony Xperia 1ii, LG Android TV, and some more devices
+- Improve detection for ARM64 CPU
+- Improve detection for Windows Mobile, Netscape, Mac on PowerPC
+- Categorize PDA as mobile
+- Fix Sharp devices misjudged as Huawei
+- Fix trailing comma for ES3 compatibility
+- Some code refactor
+
+## Version 0.7.31 / 1.0.2
+
+- Fix OPPO Reno A5 incorrect detection
+- Fix TypeError Bug
+- Use AST to extract regexes and verify them with safe-regex
+
+## Version 0.7.32 / 1.0.32
+
+- Add new browser : DuckDuckGo, Huawei Browser, LinkedIn
+- Add new OS : HarmonyOS
+- Add some Huawei models
+- Add Sharp Aquos TV
+- Improve detection Xiaomi Mi CC9
+- Fix Sony Xperia 1 III misidentified as Acer tablet
+- Fix Detect Sony BRAVIA as SmartTV
+- Fix Detect Xiaomi Mi TV as SmartTV
+- Fix Detect Galaxy Tab S8 as tablet
+- Fix WeGame mistakenly identified as WeChat
+- Fix included commas in Safari / Mobile Safari version
+- Increase UA_MAX_LENGTH to 350
+
+## Version 0.7.33 / 1.0.33
+
+- Add new browser : Cobalt
+- Identify Macintosh as an Apple device
+- Fix ReDoS vulnerability
+
+# Version 0.8
+
+Version 0.8 was created by accident. This version is now deprecated and no longer maintained, please update to version 0.7 / 1.0.
\ No newline at end of file
From 5d4ca2b4c2fec8d07218726b0bbc97abfc6c6323 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 24 Jan 2023 23:28:03 +0700
Subject: [PATCH 017/388] Fix #621 - Detect Oculus Quest Pro
---
src/ua-parser.js | 2 +-
test/device-test.json | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 9e3c71518..31bc93ae1 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -645,7 +645,7 @@
], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [
/droid.+; (wt63?0{2,3})\)/i
], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [
- /(quest( 2)?)/i // Oculus Quest
+ /(quest( 2| pro)?)/i // Oculus Quest
], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [
///////////////////
diff --git a/test/device-test.json b/test/device-test.json
index 33bfd1717..bb2952866 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1206,6 +1206,15 @@
"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",
+ "expect": {
+ "vendor": "Facebook",
+ "model": "Quest Pro",
+ "type": "wearable"
+ }
+ },
{
"desc": "OnePlus One",
"ua": "Mozilla/5.0 (Linux; Android 4.4.4; A0001 Build/KTU84Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.59 Mobile Safari/537.36",
From 1a521b867ff5b23d2a75eb5d62e10e22d78998ee Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 25 Jan 2023 01:05:50 +0700
Subject: [PATCH 018/388] Utilize navigator.userAgentData as a fallback #588
---
src/ua-parser.js | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 31bc93ae1..1ea234401 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -775,6 +775,7 @@
}
var _ua = ua || ((typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
+ var _uach = (typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgentData) ? window.navigator.userAgentData : undefined;
var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
this.getBrowser = function () {
@@ -797,6 +798,9 @@
_device[MODEL] = undefined;
_device[TYPE] = undefined;
rgxMapper.call(_device, _ua, _rgxmap.device);
+ if (!_device[TYPE] && _uach && _uach.mobile) {
+ _device[TYPE] = MOBILE;
+ }
return _device;
};
this.getEngine = function () {
@@ -811,6 +815,9 @@
_os[NAME] = undefined;
_os[VERSION] = undefined;
rgxMapper.call(_os, _ua, _rgxmap.os);
+ if (!_os[NAME] && _uach && _uach.platform != 'Unknown') {
+ _os[NAME] = _uach.platform.replace(/chrome/i, 'Chromium').replace(/mac/i, 'Mac ');
+ }
return _os;
};
this.getResult = function () {
From ba0f25c19dcdc7fb41e49b271611536d9fa7b6cf Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 26 Jan 2023 09:35:54 +0700
Subject: [PATCH 019/388] Improve Kakao/Naver detection + add test
---
src/ua-parser.js | 16 ++++-----------
test/browser-test.json | 45 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 25afb6398..34cabc461 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -58,9 +58,7 @@
SONY = 'Sony',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
- FACEBOOK = 'Facebook',
- KAKAO = 'Kakao',
- NAVER = 'Naver';
+ FACEBOOK = 'Facebook';
///////////
// Helper
@@ -226,10 +224,6 @@
/\bopr\/([\w\.]+)/i // Opera Webkit
], [VERSION, [NAME, OPERA]], [
- /Safari\/([-\w\.]+).+NAVER\((.*)\)/i, // Naver InApp for iOS
- /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp Android
- ], [VERSION,[NAME, /(.+); (.+); [0-9]+;.+/i, NAVER+' $1 $2']], [
-
// Mixed
/(kindle)\/([\w\.]+)/i, // Kindle
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
@@ -239,7 +233,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|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i,
+ /(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, aka ShouQ
/(weibo)__([\d\.]+)/i // Weibo
], [NAME, VERSION], [
@@ -290,6 +284,8 @@
// WebView
/((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android
], [[NAME, FACEBOOK], VERSION], [
+ /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App
+ /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
/safari (line)\/([\w\.]+)/i, // Line App for iOS
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
/(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
@@ -297,10 +293,6 @@
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
- /AppleWebKit\/([\w\.]+).+kakao(.*)\s+/i, // Kakao App for iOS
- / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App for Android
- ], [VERSION, [NAME, /(.+)/i, KAKAO+'$1']], [
-
/headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
], [VERSION, [NAME, CHROME+' Headless']], [
diff --git a/test/browser-test.json b/test/browser-test.json
index 2be58c21b..08f8489c0 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -1627,5 +1627,50 @@
"version": "9.0",
"major" : "9"
}
+ },
+ {
+ "desc" : "KakaoTalk App Android",
+ "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36;KAKAOTALK 2409760",
+ "expect" : {
+ "name" : "KAKAOTALK",
+ "version": "2409760",
+ "major" : "2409760"
+ }
+ },
+ {
+ "desc" : "KakaoStory App Android",
+ "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36 KAKAOSTORY/6.8.3_21046",
+ "expect" : {
+ "name" : "KAKAOSTORY",
+ "version": "6.8.3_21046",
+ "major" : "6"
+ }
+ },
+ {
+ "desc" : "KakaoTalk App iOS",
+ "ua" : "Mozilla/5.0 (iPhone; CPU; iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 BizWebView KAKAOTALK 9.7.6",
+ "expect" : {
+ "name" : "KAKAOTALK",
+ "version": "9.7.6",
+ "major" : "9"
+ }
+ },
+ {
+ "desc" : "Naver App Android",
+ "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.232 Whale/1.0.0.0 Crosswalk/26.90.3.21 Mobile Safari/537.36 NAVER(inapp; search; 1010; 11.11.2)",
+ "expect" : {
+ "name" : "NAVER",
+ "version": "11.11.2",
+ "major" : "11"
+ }
+ },
+ {
+ "desc" : "Naver App iOS",
+ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/605.1 NAVER(inapp; search; 720; 10.25.0; 11PRO)",
+ "expect" : {
+ "name" : "NAVER",
+ "version": "10.25.0",
+ "major" : "10"
+ }
}
]
From 0b0c7ff6171249f94cafbac4153ae9b9ea704e1f Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 26 Jan 2023 10:26:38 +0700
Subject: [PATCH 020/388] Fix #619 - Move Sharp up to be checked before Huawei
---
src/ua-parser.js | 9 ++++-----
test/device-test.json | 9 +++++++++
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 34cabc461..99f75077a 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -375,7 +375,6 @@
//////////////////////////
// MOBILES & TABLETS
- // Ordered by popularity
/////////////////////////
// Samsung
@@ -396,6 +395,10 @@
/(macintosh);/i
], [MODEL, [VENDOR, APPLE]], [
+ // Sharp
+ /\b(sh-?[altvz]?\d\d[a-ekm]?)/i
+ ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [
+
// Huawei
/\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i
], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
@@ -511,10 +514,6 @@
/\bmz-([-\w]{2,})/i
], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
- // Sharp
- /\b(sh-?[altvz]?\d\d[a-ekm]?)/i
- ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [
-
// MIXED
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
diff --git a/test/device-test.json b/test/device-test.json
index bb2952866..08874210a 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1764,6 +1764,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Sharp Aquos L2",
+ "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Sharp",
+ "model": "SH-L02",
+ "type": "mobile"
+ }
+ },
{
"desc": "Sharp Aquos R2",
"ua": "Mozilla/5.0 (Linux; Android 8.0; SHV42) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36",
From 0cbeb7a82966aac71e7cadb4794669dabd339868 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 26 Jan 2023 14:03:48 +0700
Subject: [PATCH 021/388] Rearrange the recently added smarttv detection
---
readme.md | 36 +++++++++---------
src/ua-parser.js | 83 +++++++++++++++++++-----------------------
test/browser-test.json | 2 +-
test/device-test.json | 9 +++++
test/os-test.json | 6 +--
5 files changed, 68 insertions(+), 68 deletions(-)
diff --git a/readme.md b/readme.md
index 71bb1879d..436f038ea 100644
--- a/readme.md
+++ b/readme.md
@@ -80,19 +80,19 @@ The methods are self explanatory, here's a small overview on all the available m
# Possible 'browser.name':
2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG,
BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera,
-Chrome Headless, Chrome WebView, Chrome, Chromium, Comodo Dragon, Dillo,
+Chrome Headless, Chrome WebView, Chrome, Chromium, Cobalt, Comodo Dragon, Dillo,
Dolphin, Doris, DuckDuckGo, Edge, Electron, Epiphany, Facebook, Falkon, Fennec,
Firebird, Firefox [Focus/Reality], Flock, Flow, GSA, GoBrowser, Huawei Browser,
ICE Browser, IE, IEMobile, IceApe, IceCat, IceDragon, Iceweasel, Instagram,
-Iridium, Iron, Jasmine, K-Meleon, Kindle, Klar, Konqueror, LBBROWSER, Line,
-LinkedIn, Links, Lunascape, Lynx, MIUI Browser, Maemo Browser, Maemo, Maxthon,
-MetaSr Midori, Minimo, Mobile Safari, Mosaic, Mozilla, NetFront, NetSurf, Netfront,
-Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, Opera Coast,
-Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, Puffin, QQ,
-QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, Safari, Sailfish Browser,
-Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, SlimBrowser, Swiftfox,
-Tesla, Tizen Browser, UCBrowser, UP.Browser, Vivaldi, Waterfox, WeChat, Weibo,
-Yandex, baidu, iCab, w3m, Whale Browser...
+Iridium, Iron, Jasmine, Kakao[Story/Talk], K-Meleon, Kindle, Klar, Konqueror,
+LBBROWSER, Line, LinkedIn, Links, Lunascape, Lynx, MIUI Browser, Maemo Browser,
+Maemo, Maxthon, MetaSr Midori, Minimo, Mobile Safari, Mosaic, Mozilla, NetFront,
+NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb,
+Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris,
+Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, Safari,
+Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim,
+SlimBrowser, Swiftfox, Tesla, Tizen Browser, UCBrowser, UP.Browser, Viera,
+Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser...
# 'browser.version' determined dynamically
```
@@ -113,10 +113,10 @@ console, mobile, tablet, smarttv, wearable, embedded
# Possible 'device.vendor':
Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell,
-Essential, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Lenovo, LG,
-Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus, OPPO, Ouya,
-Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp, Siemens,
-Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ...
+Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla,
+Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus,
+OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp,
+Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ...
# 'device.model' determined dynamically
```
@@ -141,10 +141,10 @@ AIX, Amiga OS, Android[-x86], Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS,
Contiki, Fedora, Firefox OS, FreeBSD, Debian, Deepin, DragonFly, elementary OS,
Fuchsia, Gentoo, GhostBSD, GNU, Haiku, HarmonyOS, HP-UX, Hurd, iOS, Joli, KaiOS,
Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix,
-Mint, Morph OS, NetBSD, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, PC-BSD, PCLinuxOS,
-Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS, RISC OS, Sabayon,
-Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, Ubuntu, Unix,
-VectorLinux, WebOS, Windows [Phone/Mobile], Zenwalk, ...
+Mint, Morph OS, NetBSD, NetRange, NetTV, Nintendo, OpenBSD, OpenVMS, OS/2, Palm,
+PC-BSD, PCLinuxOS, Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS,
+RISC OS, Sabayon, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen,
+Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ...
# 'os.version' determined dynamically
```
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 45ebee3fc..ec23bc94d 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -52,10 +52,7 @@
LG = 'LG',
MICROSOFT = 'Microsoft',
MOTOROLA = 'Motorola',
- NETRANGE= 'Netrange',
- NETTV = 'NetTV',
OPERA = 'Opera',
- PANASONIC = 'Panasonic',
SAMSUNG = 'Samsung',
SHARP = 'Sharp',
SONY = 'Sony',
@@ -302,8 +299,6 @@
/ wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
], [[NAME, CHROME+' WebView'], VERSION], [
- /Panasonic;(VIERA)/i // Panasonic Viera
- ], [[NAME, VIERA]], [
/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
], [VERSION, [NAME, 'Android '+BROWSER]], [
@@ -338,7 +333,8 @@
// 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
+ /(links) \(([\w\.]+)/i, // Links
+ /panasonic;(viera)/i // Panasonic Viera
], [NAME, VERSION], [
/(cobalt)\/([\w\.]+)/i // Cobalt
@@ -379,37 +375,6 @@
device : [[
- ///////////////////
- // SMARTTVS
- ///////////////////
-
- /smart-tv.+(samsung)/i // Samsung
- ], [VENDOR, [TYPE, SMARTTV]], [
- /hbbtv.+maple;(\d+)/i
- ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
- /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV
- ], [[VENDOR, LG], [TYPE, SMARTTV]], [
- /(apple) ?tv/i // Apple TV
- ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
- /crkey/i // Google Chromecast
- ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
- /droid.+aft(\w)( bui|\))/i // Fire TV
- ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
- /\(dtv[\);].+(aquos)/i,
- /(aquos-tv[\w ]+)\)/i // Sharp
- ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[
- /(bravia[\w ]+)( bui|\))/i // Sony
- ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
- /(mitv-\w{5}) bui/i // Xiaomi
- ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
- /Hbbtv.*(technisat) (.*);/i // TechniSAT
- ], [VENDOR, MODEL, [TYPE, SMARTTV]], [
- /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
- /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices
- ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
- /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
- ], [[TYPE, SMARTTV]], [
-
//////////////////////////
// MOBILES & TABLETS
/////////////////////////
@@ -539,7 +504,7 @@
// ZTE
/(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
- /(alcatel|geeksphone|nexian|panasonic|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
+ /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [
// Acer
@@ -631,6 +596,37 @@
/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i
], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [
+ ///////////////////
+ // SMARTTVS
+ ///////////////////
+
+ /smart-tv.+(samsung)/i // Samsung
+ ], [VENDOR, [TYPE, SMARTTV]], [
+ /hbbtv.+maple;(\d+)/i
+ ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
+ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV
+ ], [[VENDOR, LG], [TYPE, SMARTTV]], [
+ /(apple) ?tv/i // Apple TV
+ ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
+ /crkey/i // Google Chromecast
+ ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
+ /droid.+aft(\w)( bui|\))/i // Fire TV
+ ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
+ /\(dtv[\);].+(aquos)/i,
+ /(aquos-tv[\w ]+)\)/i // Sharp
+ ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[
+ /(bravia[\w ]+)( bui|\))/i // Sony
+ ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
+ /(mitv-\w{5}) bui/i // Xiaomi
+ ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
+ /Hbbtv.*(technisat) (.*);/i // TechniSAT
+ ], [VENDOR, MODEL, [TYPE, SMARTTV]], [
+ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
+ /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices
+ ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
+ /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
+ ], [[TYPE, SMARTTV]], [
+
///////////////////
// CONSOLES
///////////////////
@@ -745,19 +741,14 @@
], [[NAME, 'Chromium OS'], VERSION],[
// Smart TVs
- /Panasonic;VIERA/i // Panasonic Viera
- ], [[NAME, VIERA]], [
- /NETRANGEMMH/i // Netrange
- ], [[NAME, NETRANGE]], [
- /nettv\/(\d\.\d.\d)/i // NetTV
- ], [VERSION, [NAME, NETTV]], [
-
+ /panasonic;(viera)/i, // Panasonic Viera
+ /(netrange)mmh/i, // Netrange
+ /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
// Console
/(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation
/(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
-
// Other
/\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm
/(mint)[\/\(\) ]?(\w*)/i, // Mint
diff --git a/test/browser-test.json b/test/browser-test.json
index 440e4d4a7..ef0cb34f2 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -1283,7 +1283,7 @@
"ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)",
"expect" :
{
- "name" : "Viera",
+ "name" : "VIERA",
"version" : "undefined",
"major" : "undefined"
}
diff --git a/test/device-test.json b/test/device-test.json
index 8dcb4d0cc..4f44e84b4 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1476,6 +1476,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Panasonic T31",
+ "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Panasonic T31 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Mobile Safari/537.36 ",
+ "expect": {
+ "vendor": "Panasonic",
+ "model": "T31",
+ "type": "mobile"
+ }
+ },
{
"desc": "Panasonic TX-32CSW514 SmartTV",
"ua": "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)",
diff --git a/test/os-test.json b/test/os-test.json
index 3408ddd9d..e18d80861 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -1057,7 +1057,7 @@
"ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)",
"expect" :
{
- "name" : "Viera",
+ "name" : "VIERA",
"version" : "undefined"
}
},
@@ -1066,7 +1066,7 @@
"ua" : "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH",
"expect" :
{
- "name" : "Netrange",
+ "name" : "NETRANGE",
"version" : "undefined"
}
},
@@ -1075,7 +1075,7 @@
"ua" : "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70",
"expect" :
{
- "name" : "NetTV",
+ "name" : "NETTV",
"version" : "3.2.1"
}
},
From f815ca6e9cabce4099103b21423d7a9c4531c57a Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 26 Jan 2023 14:58:53 +0700
Subject: [PATCH 022/388] Fix #620 - Add new Device: Kobo
---
readme.md | 2 +-
src/ua-parser.js | 1 +
test/device-test.json | 20 +++++++++++++++++++-
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/readme.md b/readme.md
index 436f038ea..a45e17e89 100644
--- a/readme.md
+++ b/readme.md
@@ -113,7 +113,7 @@ console, mobile, tablet, smarttv, wearable, embedded
# Possible 'device.vendor':
Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell,
-Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla,
+Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Kobo,
Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus,
OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp,
Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ...
diff --git a/src/ua-parser.js b/src/ua-parser.js
index ec23bc94d..4a22f671c 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -527,6 +527,7 @@
/(oppo) ?([\w ]+) bui/i // OPPO
], [VENDOR, MODEL, [TYPE, MOBILE]], [
+ /(kobo)\s(ereader|touch)/i, // Kobo
/(archos) (gamepad2?)/i, // Archos
/(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
/(kindle)\/([\w\.]+)/i, // Kindle
diff --git a/test/device-test.json b/test/device-test.json
index 4f44e84b4..f042a17db 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -817,6 +817,24 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Kobo eReader",
+ "ua": "Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Kobo eReader Safari/538.1",
+ "expect": {
+ "vendor": "Kobo",
+ "model": "eReader",
+ "type": "tablet"
+ }
+ },
+ {
+ "desc": "Kobo Touch",
+ "ua": "Mozilla/5.0 (Linux; U; Android 2.0; en-us;) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/538.1 (Kobo Touch 0377/4.20.14622)",
+ "expect": {
+ "vendor": "Kobo",
+ "model": "Touch",
+ "type": "tablet"
+ }
+ },
{
"desc": "Lenovo Tab 2",
"ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36",
@@ -826,7 +844,7 @@
"type": "tablet"
}
},
- {
+ {
"desc": "Lenovo Phone",
"ua": "Mozilla/5.0 (Linux; Android 6.0; Lenovo PB2-650M Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/311.0.0.44.117;]",
"expect": {
From f8e5a1fb4fbe55b781a48d7d141fdc41ba93fa38 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 31 Jan 2023 15:09:14 +0700
Subject: [PATCH 023/388] Fix #601 - Detect Chrome OS without version
---
src/ua-parser.js | 2 +-
test/os-test.json | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 031faa362..ac7cbd3cb 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -738,7 +738,7 @@
// Google Chromecast
/crkey\/([\d\.]+)/i // Google Chromecast
], [VERSION, [NAME, CHROME+'cast']], [
- /(cros) [\w]+ ([\w\.]+\w)/i // Chromium OS
+ /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS
], [[NAME, 'Chromium OS'], VERSION],[
// Smart TVs
diff --git a/test/os-test.json b/test/os-test.json
index e18d80861..20670158d 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -665,6 +665,15 @@
"version" : "1.9.2.22-0.1mdv2010.2"
}
},
+ {
+ "desc" : "Chrome OS",
+ "ua" : "Mozilla/5.0 (X11; CrOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36",
+ "expect" :
+ {
+ "name" : "Chromium OS",
+ "version" : "undefined"
+ }
+ },
{
"desc" : "Chromium OS",
"ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
From 6c34c3d4faddc9fee686ef6161bc16f5d0882045 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 14 Feb 2023 23:28:09 +0700
Subject: [PATCH 024/388] Add new utility methods: is() & toString()
---
dist/ua-parser.min.js | 4 +-
dist/ua-parser.pack.js | 4 +-
readme.md | 147 +++++++++++++++++++++++++++++------------
src/ua-parser.js | 115 +++++++++++++++++++++++---------
test/test.js | 98 ++++++++++++++++++++++++++-
5 files changed, 290 insertions(+), 78 deletions(-)
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index d5869863f..767bddce0 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
-/* UAParser.js v0.7.31
+/* UAParser.js v0.7.32
Copyright © 2012-2021 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="0.7.31",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",UA_MAX_LENGTH=350;var AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",BROWSER="Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",OPERA="Opera",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",FACEBOOK="Facebook";var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){mergedRegexes[i]=extensions[i].concat(regexes[i])}else{mergedRegexes[i]=regexes[i]}}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;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;jUA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this};this.setUA(_ua);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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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="0.7.32",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",UA_MAX_LENGTH=350;var AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",BROWSER="Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",OPERA="Opera",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",FACEBOOK="Facebook";var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){mergedRegexes[i]=extensions[i].concat(regexes[i])}else{mergedRegexes[i]=regexes[i]}}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;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;jUA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this};this.setUA(_ua);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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 18c0cdcd6..5c33553bd 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
-/* UAParser.js v0.7.31
+/* UAParser.js v0.7.32
Copyright © 2012-2021 Faisal Salman
MIT License */
-!function(r,d){"use strict";function i(i){for(var e={},o=0;oS?L(i,S):i,this},this.setUA(o),this}Q.prototype.is=function(i){for(var e in this.propIs)if(o(this[this.propIs[e]],this.rgxIs)==o(i,this.rgxIs))return!0;return!1},Q.prototype.toString=function(){var i,e="";for(i in this.propToString)typeof this[this.propToString[i]]!==s&&(e+=(e?" ":"")+this[this.propToString[i]]);return e||s},Y.prototype=new Q([l,h],[[l],/\s?browser$/i]),J.prototype=new Q([f],[[f]]),ii.prototype=new Q([m,r],[[p,r,m]]),ei.prototype=new Q([l,h],[[l]]),oi.prototype=new Q([l,h],[[l],/\s?os$/i]),ti.VERSION="0.7.32",ti.BROWSER=i([l,h,w]),ti.CPU=i([f]),ti.DEVICE=i([r,m,p,g,v,k,x,y,_]),ti.ENGINE=ti.OS=i([l,h]),typeof exports!==s?(typeof module!==s&&module.exports&&(exports=module.exports=ti),exports.UAParser=ti):typeof define==u&&define.amd?define(function(){return ti}):typeof a!==s&&(a.UAParser=ti);var ai,ni=typeof a!==s&&(a.jQuery||a.Zepto);ni&&!ni.ua&&(ai=new ti,ni.ua=ai.getResult(),ni.ua.get=function(){return ai.getUA()},ni.ua.set=function(i){ai.setUA(i);var e,o=ai.getResult();for(e in o)ni.ua[e]=o[e]})}("object"==typeof window?window:this);
\ No newline at end of file
diff --git a/readme.md b/readme.md
index a45e17e89..514e949d9 100644
--- a/readme.md
+++ b/readme.md
@@ -19,10 +19,7 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
* Source : https://github.com/faisalman/ua-parser-js
# Documentation
-### UAParser([user-agent][,extensions])
-typeof `user-agent` "string".
-
-typeof `extensions` "array".
+### UAParser([user-agent:string][,extensions:object])
In The Browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in nodejs for the function to work.
Usually you can find the user agent in:
@@ -32,7 +29,7 @@ Usually you can find the user agent in:
## Constructor
When you call `UAParser` with the `new` keyword `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string.
Like so:
-* `new UAParser([uastring][,extensions])`
+* `new UAParser([user-agent:string][,extensions:object])`
```js
let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs
console.log(parser); // {}
@@ -49,7 +46,7 @@ console.log(parserResults);
```
When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
-* `UAParser([uastring][,extensions])`
+* `UAParser([user-agent:string][,extensions:object])`
* returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
## Methods
@@ -67,13 +64,12 @@ The methods are self explanatory, here's a small overview on all the available m
* `getUA()` - returns the user-agent string.
* `setUA(user-agent)` - set a custom user-agent to be parsed.
-
---
-* `getResult()`
- * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
+* `getResult() : UAResult`
+ * returns `{ ua: '', browser: UABrowser {}, cpu: UACPU {}, device: UADevice {}, engine: UAEngine {}, os: UAOS {} }`
-* `getBrowser()`
+* `getBrowser() : UABrowser`
* returns `{ name: '', version: '' }`
```sh
@@ -97,7 +93,7 @@ Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser...
# 'browser.version' determined dynamically
```
-* `getDevice()`
+* `getDevice() : UADevice`
* returns `{ model: '', type: '', vendor: '' }`
```sh
@@ -121,7 +117,7 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE
# 'device.model' determined dynamically
```
-* `getEngine()`
+* `getEngine() : UAEngine`
* returns `{ name: '', version: '' }`
```sh
@@ -132,7 +128,7 @@ NetSurf, Presto, Tasman, Trident, w3m, WebKit
# 'engine.version' determined dynamically
```
-* `getOS()`
+* `getOS() : UAOS`
* returns `{ name: '', version: '' }`
```sh
@@ -149,7 +145,7 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ...
# 'os.version' determined dynamically
```
-* `getCPU()`
+* `getCPU() : UACPU`
* returns `{ architecture: '' }`
```sh
@@ -157,13 +153,92 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ...
68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64]
```
-* `getUA()`
+* `getUA() : string`
* returns UA string of current instance
* `setUA(uastring)`
* set UA string to be parsed
* returns current instance
+#### * is() utility `since@1.1`
+
+```js
+let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
+
+uap.getBrowser().name; // "IEMobile"
+uap.getBrowser().is("IEMobile"); // true
+uap.getCPU().is("ARM"); // true
+
+uap.getOS().name; // "Windows Phone"
+uap.getOS().is("Windows Phone"); // true
+
+uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" }
+uap.getResult().device; // { vendor: "Nokia", model: "Lumia 635", type: "mobile" }
+
+uap.getDevice().is("mobile"); // true
+uap.getDevice().is("Lumia 635"); // true
+uap.getDevice().is("Nokia"); // true
+uap.getDevice().is("iPhone"); // false
+uap.getResult().device.is("Nokia"); // true
+uap.getResult().device.model; // "Lumia 635"
+
+uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
+uap.getBrowser().is("IEMobile"); // false
+uap.getBrowser().is("Chrome"); // true
+uap.getResult().browser.is("Edge"); // false
+uap.getResult().os.is("Mac OS"); // true
+uap.getResult().os.version; // "10.6.8"
+uap.getEngine().is("Blink"); // true
+```
+
+#### * toString() utility `since@1.1`
+
+```js
+let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
+
+uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" }
+uap.getDevice().toString(); // "Nokia Lumia 635"
+
+uap.getResult().os.name; // "Windows Phone"
+uap.getResult().os.version; // "8.1"
+uap.getResult().os.toString(); // "Windows Phone 8.1"
+
+uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
+uap.getBrowser().name; // Chrome
+uap.getBrowser().version; // 28.0.1500.95
+uap.getBrowser().major; // 28
+uap.getBrowser().toString(); // "Chrome 28.0.1500.95"
+```
+
+## Extending Regex
+
+If you want to detect something that's not currently provided by UAParser.js (eg: bots, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own.
+
+* `UAParser([uastring,] extensions)`
+
+```js
+// Example:
+let myOwnListOfBrowsers = [
+ [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION]
+];
+let myParser = new UAParser({ browser: myOwnListOfBrowsers });
+let myUA = 'Mozilla/5.0 MyBrowser/1.3';
+console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3"}
+
+// Another example:
+let myOwnListOfDevices = [
+ [/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]],
+ [/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]]
+];
+let myParser2 = new UAParser({
+ browser: myOwnListOfBrowsers,
+ device: myOwnListOfDevices
+});
+let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max';
+console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}
+```
+
+
# Usage
## Using HTML
@@ -175,8 +250,8 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ...
@@ -248,11 +323,11 @@ $ npm install ua-parser-js
```js
var http = require('http');
-var parser = require('ua-parser-js');
+var uap = require('ua-parser-js');
http.createServer(function (req, res) {
// get user-agent header
- var ua = parser(req.headers['user-agent']);
+ var ua = uap(req.headers['user-agent']);
// write the result as response
res.end(JSON.stringify(ua, null, ' '));
})
@@ -296,20 +371,6 @@ console.log(parseInt($.ua.browser.version.split('.')[0], 10)); // 4
$('body').addClass('ua-browser-' + $.ua.browser.name + ' ua-devicetype-' + $.ua.device.type);
```
-## Using Extension
-
-* `UAParser([uastring,] extensions)`
-
-```js
-// Example:
-var myOwnListOfBrowsers = [
- [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION]
-];
-var myParser = new UAParser({ browser: myOwnListOfBrowsers });
-var myUA = 'Mozilla/5.0 MyBrowser/1.3';
-console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3"}
-```
-
# Development
## Sponsors
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 4a22f671c..c38b14d99 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -17,7 +17,7 @@
/////////////
- var LIBVERSION = '0.7.32',
+ var LIBVERSION = '1.1.34',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -56,7 +56,6 @@
SAMSUNG = 'Samsung',
SHARP = 'Sharp',
SONY = 'Sony',
- VIERA = 'Viera',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
FACEBOOK = 'Facebook';
@@ -87,11 +86,14 @@
return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
},
lowerize = function (str) {
- return str.toLowerCase();
+ return typeof(str) === STR_TYPE ? str.toLowerCase() : UNDEF_TYPE;
},
majorize = function (version) {
return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined;
},
+ sanitize = function (str, rgx) {
+ return rgx ? lowerize(str).replace(rgx, EMPTY) : lowerize(str);
+ },
trim = function (str, len) {
if (typeof(str) === STR_TYPE) {
str = str.replace(/^\s\s*/, EMPTY);
@@ -200,6 +202,10 @@
'8.1' : 'NT 6.3',
'10' : ['NT 6.4', 'NT 10.0'],
'RT' : 'ARM'
+ },
+ archEquivalenceMap = {
+ 'amd64' : ['x86-64', 'x64'],
+ 'ia32' : ['x86']
};
//////////////
@@ -775,7 +781,70 @@
// Constructor
////////////////
- var UAParser = function (ua, extensions) {
+ function UAItem (propToString, propIs) {
+ this.propToString = propToString;
+ this.propIs = propIs[0];
+ this.rgxIs = propIs[1];
+ }
+ UAItem.prototype.is = function (strCheck) {
+ for (var i in this.propIs) {
+ if (sanitize(this[this.propIs[i]], this.rgxIs) == sanitize(strCheck, this.rgxIs)) {
+ return true;
+ }
+ }
+ return false;
+ };
+ UAItem.prototype.toString = function () {
+ var str = '';
+ for (var i in this.propToString) {
+ if (typeof(this[this.propToString[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : '') + this[this.propToString[i]];
+ }
+ }
+ return str ? str : UNDEF_TYPE;
+ };
+
+ function UABrowser () {
+ this[NAME] = undefined;
+ this[VERSION] = undefined;
+ this[MAJOR] = undefined;
+ }
+ UABrowser.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?browser$/i]);
+
+ function UACPU () {
+ this[ARCHITECTURE] = undefined;
+ }
+ UACPU.prototype = new UAItem([ARCHITECTURE], [[ARCHITECTURE]]);
+
+ function UADevice () {
+ this[VENDOR] = undefined;
+ this[MODEL] = undefined;
+ this[TYPE] = undefined;
+ }
+ UADevice.prototype = new UAItem([VENDOR, MODEL], [[TYPE, MODEL, VENDOR]]);
+
+ function UAEngine () {
+ this[NAME] = undefined;
+ this[VERSION] = undefined;
+ }
+ UAEngine.prototype = new UAItem([NAME, VERSION], [[NAME]]);
+
+ function UAOS () {
+ this[NAME] = undefined;
+ this[VERSION] = undefined;
+ }
+ UAOS.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?os$/i]);
+
+ function UAResult (uap) {
+ this.ua = uap.getUA();
+ this.browser = uap.getBrowser();
+ this.cpu = uap.getCPU();
+ this.device = uap.getDevice();
+ this.engine = uap.getEngine();
+ this.os = uap.getOS();
+ }
+
+ function UAParser (ua, extensions) {
if (typeof ua === OBJ_TYPE) {
extensions = ua;
@@ -790,25 +859,20 @@
var _uach = (typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgentData) ? window.navigator.userAgentData : undefined;
var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
+ // public methods
this.getBrowser = function () {
- var _browser = {};
- _browser[NAME] = undefined;
- _browser[VERSION] = undefined;
+ var _browser = new UABrowser();
rgxMapper.call(_browser, _ua, _rgxmap.browser);
- _browser.major = majorize(_browser.version);
+ _browser[MAJOR] = majorize(_browser[VERSION]);
return _browser;
};
this.getCPU = function () {
- var _cpu = {};
- _cpu[ARCHITECTURE] = undefined;
+ var _cpu = new UACPU();
rgxMapper.call(_cpu, _ua, _rgxmap.cpu);
return _cpu;
};
this.getDevice = function () {
- var _device = {};
- _device[VENDOR] = undefined;
- _device[MODEL] = undefined;
- _device[TYPE] = undefined;
+ var _device = new UADevice();
rgxMapper.call(_device, _ua, _rgxmap.device);
if (!_device[TYPE] && _uach && _uach.mobile) {
_device[TYPE] = MOBILE;
@@ -816,31 +880,22 @@
return _device;
};
this.getEngine = function () {
- var _engine = {};
- _engine[NAME] = undefined;
- _engine[VERSION] = undefined;
+ var _engine = new UAEngine();
rgxMapper.call(_engine, _ua, _rgxmap.engine);
return _engine;
};
this.getOS = function () {
- var _os = {};
- _os[NAME] = undefined;
- _os[VERSION] = undefined;
+ var _os = new UAOS();
rgxMapper.call(_os, _ua, _rgxmap.os);
if (!_os[NAME] && _uach && _uach.platform != 'Unknown') {
- _os[NAME] = _uach.platform.replace(/chrome/i, 'Chromium').replace(/mac/i, 'Mac ');
+ _os[NAME] = _uach.platform
+ .replace(/chrome os/i, 'Chromium OS')
+ .replace(/macos/i, 'Mac OS'); // backward compatibility
}
return _os;
};
this.getResult = function () {
- return {
- ua : this.getUA(),
- browser : this.getBrowser(),
- engine : this.getEngine(),
- os : this.getOS(),
- device : this.getDevice(),
- cpu : this.getCPU()
- };
+ return new UAResult(this);
};
this.getUA = function () {
return _ua;
@@ -851,7 +906,7 @@
};
this.setUA(_ua);
return this;
- };
+ }
UAParser.VERSION = LIBVERSION;
UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]);
diff --git a/test/test.js b/test/test.js
index e1fd175d2..18ec18904 100644
--- a/test/test.js
+++ b/test/test.js
@@ -79,7 +79,7 @@ for (var i in methods) {
describe('Returns', function () {
it('getResult() should returns JSON', function(done) {
- assert.deepStrictEqual(new UAParser('').getResult(),
+ assert.deepEqual(new UAParser('').getResult(),
{
ua : '',
browser: { name: undefined, version: undefined, major: undefined },
@@ -166,3 +166,99 @@ describe('Testing regexes', function () {
});
});
});
+
+
+describe('is() utility method', function () {
+ let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
+
+ it('Should match full name', function () {
+ assert.strictEqual(uap.getBrowser().name, "IEMobile");
+ assert.strictEqual(uap.getBrowser().is("IEMobile"), true);
+ assert.strictEqual(uap.getBrowser().is("IE"), false);
+
+ it('Should ignore spaces and "Browser" suffix', function () {
+ assert.strictEqual(uap.getBrowser().is("I EMo b i l e Browser"), true);
+ });
+ });
+
+ it('Should ignore case', function () {
+ assert.strictEqual(uap.getEngine().name, "Trident");
+ assert.strictEqual(uap.getEngine().is("tRiDeNt"), true);
+ });
+
+ it('Should get exact name', function () {
+ assert.strictEqual(uap.getOS().name, "Windows Phone");
+ assert.strictEqual(uap.getOS().is("Windows Phone"), true);
+ assert.strictEqual(uap.getOS().is("Windows Phone OS"), true);
+ assert.strictEqual(uap.getOS().is("Windows Mobile"), false);
+ assert.strictEqual(uap.getOS().is("Android"), false);
+ });
+
+ it('Should check all device properties', function () {
+ assert.deepEqual(uap.getDevice(), {
+ vendor : "Nokia",
+ model : "Lumia 635",
+ type : "mobile"
+ });
+ assert.strictEqual(uap.getDevice().is("Nokia"), true);
+ assert.strictEqual(uap.getDevice().is("Lumia 635"), true);
+ assert.strictEqual(uap.getDevice().is("mobile"), true);
+
+ assert.strictEqual(uap.getResult().device.is("Nokia"), true);
+
+ uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15");
+ assert.strictEqual(uap.getDevice().is("undefined"), true);
+ });
+
+ it('Should get result after reassignment', function () {
+ uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
+ assert.strictEqual(uap.getOS().name, "Mac OS");
+ assert.strictEqual(uap.getOS().is("Mac OS"), true);
+ assert.strictEqual(uap.getOS().is("M ac"), false);
+ assert.strictEqual(uap.getOS().is("macOS"), true);
+ assert.strictEqual(uap.getOS().is("mac OS"), true);
+ assert.strictEqual(uap.getOS().is("M a c "), false);
+ assert.strictEqual(uap.getOS().is("Mac OS OS"), false);
+ assert.strictEqual(uap.getOS().is("Mac OS X"), false);
+
+ assert.strictEqual(uap.getBrowser().is("Chrome"), true);
+ assert.strictEqual(uap.getEngine().is("Blink"), true);
+ });
+
+ it('Should accept arch equivalent name', function () {
+ uap.setUA("Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0");
+ assert.strictEqual(uap.getCPU().architecture, "ia32");
+ assert.strictEqual(uap.getCPU().is("ia32"), true);
+ assert.strictEqual(uap.getCPU().is("x86"), false);
+
+ uap.setUA("Opera/9.80 (X11; Linux x86_64; U; Linux Mint; en) Presto/2.2.15 Version/10.10");
+ assert.strictEqual(uap.getCPU().architecture, "amd64");
+ assert.strictEqual(uap.getCPU().is("amd64"), true);
+ assert.strictEqual(uap.getCPU().is("x86-64"), false);
+ assert.strictEqual(uap.getCPU().is("x64"), false);
+ });
+});
+
+describe('toString() utility method', function () {
+ let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
+ assert.strictEqual(uap.getBrowser().name, "IEMobile");
+ assert.strictEqual(uap.getBrowser().version, "11.0");
+ assert.strictEqual(uap.getBrowser().major, "11");
+ assert.strictEqual(uap.getBrowser().toString(), "IEMobile 11.0");
+
+ assert.strictEqual(uap.getCPU().architecture, "arm");
+ assert.strictEqual(uap.getCPU().toString(), "arm");
+
+ assert.strictEqual(uap.getDevice().vendor, "Nokia");
+ assert.strictEqual(uap.getDevice().model, "Lumia 635");
+ assert.strictEqual(uap.getDevice().type, "mobile");
+ assert.strictEqual(uap.getDevice().toString(), "Nokia Lumia 635");
+
+ assert.strictEqual(uap.getEngine().name, "Trident");
+ assert.strictEqual(uap.getEngine().version, "7.0");
+ assert.strictEqual(uap.getEngine().toString(), "Trident 7.0");
+
+ assert.strictEqual(uap.getOS().name, "Windows Phone");
+ assert.strictEqual(uap.getOS().version, "8.1");
+ assert.strictEqual(uap.getOS().toString(), "Windows Phone 8.1");
+});
\ No newline at end of file
From 746ac28f94457f26746f1c54fbd17441d88adaca Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 19 Feb 2023 16:11:07 +0700
Subject: [PATCH 025/388] Readme: update is() & toString() methods explanation
---
readme.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 7 deletions(-)
diff --git a/readme.md b/readme.md
index 514e949d9..cfc05c43e 100644
--- a/readme.md
+++ b/readme.md
@@ -163,6 +163,32 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ...
#### * is() utility `since@1.1`
```js
+// Is just a shorthand to check whether one of the specified properties has equal value
+// so that instead of write it using `==` operator like this:
+
+let ua = UAParser();
+let device = ua.device;
+let os = ua.os;
+
+if (device.type == "mobile" && os.name != "iOS") {}
+if (device.type == "smarttv" || device.vendor == "Samsung") {}
+
+// we can also write the comparison above into as follow:
+
+if (device.is("mobile") && !os.is("iOS")) {}
+if (device.is("smarttv") || device.is("Samsung")) {}
+
+/*
+ Properties will be checked in this particular order:
+ * browser : name
+ * cpu : architecture
+ * device : type, model, vendor
+ * engine : name
+ * os : name
+*/
+
+// Another examples:
+
let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
uap.getBrowser().name; // "IEMobile"
@@ -183,20 +209,43 @@ uap.getResult().device.is("Nokia"); // true
uap.getResult().device.model; // "Lumia 635"
uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
-uap.getBrowser().is("IEMobile"); // false
-uap.getBrowser().is("Chrome"); // true
+
+let browser = uap.getBrowser();
+browser.is("IEMobile"); // false
+browser.is("Chrome"); // true
+
uap.getResult().browser.is("Edge"); // false
+uap.getResult().os.name // "Mac OS"
uap.getResult().os.is("Mac OS"); // true
uap.getResult().os.version; // "10.6.8"
-uap.getEngine().is("Blink"); // true
+
+let engine = uap.getEngine();
+engine.is("Blink"); // true
```
#### * toString() utility `since@1.1`
```js
+// Retrieve full-name values as a string
+
+/*
+ Values will be concatenated following this pattern:
+ * browser : name + version
+ * cpu : architecture
+ * device : vendor + model
+ * engine : name + version
+ * os : name + version
+*/
+
+// Usage examples
+
let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
-uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" }
+uap.getDevice(); // {
+ // vendor: "Nokia",
+ // model: "Lumia 635",
+ // type: "mobile"
+ // }
uap.getDevice().toString(); // "Nokia Lumia 635"
uap.getResult().os.name; // "Windows Phone"
@@ -204,10 +253,15 @@ uap.getResult().os.version; // "8.1"
uap.getResult().os.toString(); // "Windows Phone 8.1"
uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
-uap.getBrowser().name; // Chrome
-uap.getBrowser().version; // 28.0.1500.95
-uap.getBrowser().major; // 28
+uap.getBrowser().name; // "Chrome"
+uap.getBrowser().version; // "28.0.1500.95"
+uap.getBrowser().major; // "28"
uap.getBrowser().toString(); // "Chrome 28.0.1500.95"
+
+let engine = uap.getEngine();
+engine.name; // "Blink"
+engine.version; // "28.0.1500.95"
+engine.toString(); // "Blink 28.0.1500.95"
```
## Extending Regex
From f18516c9c8b858d8c10405f74b0403a53db5ef1f Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 19 Feb 2023 16:43:59 +0700
Subject: [PATCH 026/388] Make sure all properties are "undefined" for
is("undefined") to be true
---
src/ua-parser.js | 9 +++++++--
test/test.js | 14 ++++++++++----
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index cb60dfcee..18ac0f6f1 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -787,12 +787,17 @@
this.rgxIs = propIs[1];
}
UAItem.prototype.is = function (strCheck) {
+ var is = false;
for (var i in this.propIs) {
if (sanitize(this[this.propIs[i]], this.rgxIs) == sanitize(strCheck, this.rgxIs)) {
- return true;
+ is = true;
+ if (strCheck != UNDEF_TYPE) break;
+ } else if (strCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
}
}
- return false;
+ return is;
};
UAItem.prototype.toString = function () {
var str = '';
diff --git a/test/test.js b/test/test.js
index a85e9daad..622df973b 100644
--- a/test/test.js
+++ b/test/test.js
@@ -205,9 +205,6 @@ describe('is() utility method', function () {
assert.strictEqual(uap.getDevice().is("mobile"), true);
assert.strictEqual(uap.getResult().device.is("Nokia"), true);
-
- uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15");
- assert.strictEqual(uap.getDevice().is("undefined"), true);
});
it('Should get result after reassignment', function () {
@@ -225,7 +222,16 @@ describe('is() utility method', function () {
assert.strictEqual(uap.getEngine().is("Blink"), true);
});
- it('Should accept arch equivalent name', function () {
+ it('Should refrain from "undefined" until all properties are checked', function () {
+ assert.strictEqual(uap.getDevice().is("undefined"), false);
+ assert.strictEqual(uap.getDevice().is("Apple"), true);
+
+ uap.setUA("");
+ assert.strictEqual(uap.getDevice().is("undefined"), true);
+ });
+
+ //it('Should accept arch equivalent name', function () {
+ it('Should accept exact arch name', function () {
uap.setUA("Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0");
assert.strictEqual(uap.getCPU().architecture, "ia32");
assert.strictEqual(uap.getCPU().is("ia32"), true);
From d03c74d6b7b7fb0da3a064337116c7b52579cc60 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 21 Feb 2023 22:12:30 +0700
Subject: [PATCH 027/388] Insert sponsorship content from 51degrees
---
images/51degrees.svg | 19 +++++++++++++++++++
readme.md | 24 ++++++++++++++++++++++--
2 files changed, 41 insertions(+), 2 deletions(-)
create mode 100644 images/51degrees.svg
diff --git a/images/51degrees.svg b/images/51degrees.svg
new file mode 100644
index 000000000..c3393f646
--- /dev/null
+++ b/images/51degrees.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
index cfc05c43e..4bae24fa3 100644
--- a/readme.md
+++ b/readme.md
@@ -18,6 +18,25 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
* Demo : https://faisalman.github.io/ua-parser-js
* Source : https://github.com/faisalman/ua-parser-js
+***
+
+### From Our Sponsors:
+
+
+
+
+
+
+↗ @51degrees/ua-parser-js
+
+
+UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.
This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).
↗ Visit 51Degrees UAParser to get started.
+
+
+
+
+---
+
# Documentation
### UAParser([user-agent:string][,extensions:object])
@@ -427,9 +446,10 @@ $('body').addClass('ua-browser-' + $.ua.browser.name + ' ua-devicetype-' + $.ua.
# Development
-## Sponsors
+## Backers & Sponsors
-
+
+
From 03b0a5afa60d472dfff160185c493cb94f1e0298 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 22 Feb 2023 20:52:27 +0700
Subject: [PATCH 028/388] Fix #498 - Detect Brave Browser by checking
navigator.brave
https://github.com/brave/brave-browser/issues/10165#issuecomment-641128278
---
src/ua-parser.js | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 18ac0f6f1..71a5c9208 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -860,8 +860,9 @@
return new UAParser(ua, extensions).getResult();
}
- var _ua = ua || ((typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
- var _uach = (typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgentData) ? window.navigator.userAgentData : undefined;
+ var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined;
+ var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : EMPTY);
+ var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined;
var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
// public methods
@@ -869,6 +870,10 @@
var _browser = new UABrowser();
rgxMapper.call(_browser, _ua, _rgxmap.browser);
_browser[MAJOR] = majorize(_browser[VERSION]);
+ // Brave-specific detection
+ if (_navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) {
+ _browser[NAME] = 'Brave';
+ }
return _browser;
};
this.getCPU = function () {
From 5ea9f0ec16fcbe53f59e5d3a95d237e179a0539c Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Fri, 24 Feb 2023 14:25:30 +0700
Subject: [PATCH 029/388] Fix #387 #554 - Detect iPadOS 13
https://stackoverflow.com/questions/9038625/detect-if-device-is-ios
---
src/ua-parser.js | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 71a5c9208..a3a3ad414 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -58,7 +58,9 @@
SONY = 'Sony',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
- FACEBOOK = 'Facebook';
+ FACEBOOK = 'Facebook',
+ CHROMIUM_OS = 'Chromium OS',
+ MAC_OS = 'Mac OS';
///////////
// Helper
@@ -721,7 +723,7 @@
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i,
/(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS
- ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [
+ ], [[NAME, MAC_OS], [VERSION, /_/g, '.']], [
// Mobile OSes
/droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS
@@ -745,7 +747,7 @@
/crkey\/([\d\.]+)/i // Google Chromecast
], [VERSION, [NAME, CHROME+'cast']], [
/(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS
- ], [[NAME, 'Chromium OS'], VERSION],[
+ ], [[NAME, CHROMIUM_OS], VERSION],[
// Smart TVs
/panasonic;(viera)/i, // Panasonic Viera
@@ -887,6 +889,11 @@
if (!_device[TYPE] && _uach && _uach.mobile) {
_device[TYPE] = MOBILE;
}
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (_device[MODEL] == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {
+ _device[MODEL] = 'iPad';
+ _device[TYPE] = TABLET;
+ }
return _device;
};
this.getEngine = function () {
@@ -899,8 +906,8 @@
rgxMapper.call(_os, _ua, _rgxmap.os);
if (!_os[NAME] && _uach && _uach.platform != 'Unknown') {
_os[NAME] = _uach.platform
- .replace(/chrome os/i, 'Chromium OS')
- .replace(/macos/i, 'Mac OS'); // backward compatibility
+ .replace(/chrome os/i, CHROMIUM_OS)
+ .replace(/macos/i, MAC_OS); // backward compatibility
}
return _os;
};
From 18730452f2b40099710613ecbe188d98f4bfad19 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 25 Feb 2023 11:41:19 +0700
Subject: [PATCH 030/388] Add new device: Apple Watch, new os: watchOS
---
readme.md | 15 ++++++++-------
src/ua-parser.js | 4 ++++
test/device-test.json | 9 +++++++++
test/os-test.json | 27 +++++++++++++++++++++++++++
4 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/readme.md b/readme.md
index 4bae24fa3..fdeccfd4d 100644
--- a/readme.md
+++ b/readme.md
@@ -159,7 +159,7 @@ Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix,
Mint, Morph OS, NetBSD, NetRange, NetTV, Nintendo, OpenBSD, OpenVMS, OS/2, Palm,
PC-BSD, PCLinuxOS, Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS,
RISC OS, Sabayon, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen,
-Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ...
+Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwalk, ...
# 'os.version' determined dynamically
```
@@ -182,7 +182,7 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ...
#### * is() utility `since@1.1`
```js
-// Is just a shorthand to check whether one of the specified properties has equal value
+// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive)
// so that instead of write it using `==` operator like this:
let ua = UAParser();
@@ -198,7 +198,7 @@ if (device.is("mobile") && !os.is("iOS")) {}
if (device.is("smarttv") || device.is("Samsung")) {}
/*
- Properties will be checked in this particular order:
+ Each properties will be checked in this particular order:
* browser : name
* cpu : architecture
* device : type, model, vendor
@@ -220,10 +220,11 @@ uap.getOS().is("Windows Phone"); // true
uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" }
uap.getResult().device; // { vendor: "Nokia", model: "Lumia 635", type: "mobile" }
-uap.getDevice().is("mobile"); // true
-uap.getDevice().is("Lumia 635"); // true
-uap.getDevice().is("Nokia"); // true
-uap.getDevice().is("iPhone"); // false
+let device = uap.getDevice();
+device.is("mobile"); // true
+device.is("Lumia 635"); // true
+device.is("Nokia"); // true
+device.is("iPhone"); // false
uap.getResult().device.is("Nokia"); // true
uap.getResult().device.model; // "Lumia 635"
diff --git a/src/ua-parser.js b/src/ua-parser.js
index a3a3ad414..f03bfc525 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -656,6 +656,8 @@
/((pebble))app/i // Pebble
], [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
@@ -742,6 +744,8 @@
/web0s;.+rt(tv)/i,
/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS
], [VERSION, [NAME, 'webOS']], [
+ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS
+ ], [VERSION, [NAME, 'watchOS']], [
// Google Chromecast
/crkey\/([\d\.]+)/i // Google Chromecast
diff --git a/test/device-test.json b/test/device-test.json
index f042a17db..c40361f13 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -763,6 +763,15 @@
"type": "undefined"
}
},
+ {
+ "desc": "Apple Watch",
+ "ua": "atc/1.0 watchOS/7.3.3 model/Watch4,2 hwp/t8006 build/18S830 (6; dt:191)",
+ "expect": {
+ "vendor": "Apple",
+ "model": "watch",
+ "type": "wearable"
+ }
+ },
{
"desc": "iPad using UCBrowser",
"ua": "Mozilla/5.0 (iPad; U; CPU OS 11_2 like Mac OS X; zh-CN; iPad5,3) AppleWebKit/534.46 (KHTML, like Gecko) UCBrowser/3.0.1.776 U3/ Mobile/10A403 Safari/7543.48.3",
diff --git a/test/os-test.json b/test/os-test.json
index 20670158d..d97c7ffce 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -764,6 +764,33 @@
"version" : "undefined"
}
},
+ {
+ "desc" : "watchOS",
+ "ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]",
+ "expect" :
+ {
+ "name" : "watchOS",
+ "version" : "8.4"
+ }
+ },
+ {
+ "desc" : "watchOS",
+ "ua" : "atc/1.0 watchOS/7.4.1 model/Watch3,3 hwp/t8004 build/18T201 (6; dt:155)",
+ "expect" :
+ {
+ "name" : "watchOS",
+ "version" : "7.4.1"
+ }
+ },
+ {
+ "desc" : "watchOS",
+ "ua" : "Watch4,3/5.3.8 (16U680)",
+ "expect" :
+ {
+ "name" : "watchOS",
+ "version" : "5.3.8"
+ }
+ },
{
"desc" : "Mac OS on PowerPC",
"ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)",
From f1449a8202782d13dc5f437617d07143d0934ef0 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 26 Feb 2023 18:27:55 +0700
Subject: [PATCH 031/388] Refactor
---
src/ua-parser.js | 219 +++++++++++++++++++++++++++++------------------
test/test.js | 4 +-
2 files changed, 137 insertions(+), 86 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index f03bfc525..188fbcbc9 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -56,8 +56,10 @@
SAMSUNG = 'Samsung',
SHARP = 'Sharp',
SONY = 'Sony',
+ SWISS = 'Swiss',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
+ ZTE = 'ZTE',
FACEBOOK = 'Facebook',
CHROMIUM_OS = 'Chromium OS',
MAC_OS = 'Mac OS';
@@ -87,15 +89,12 @@
has = function (str1, str2) {
return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
},
- lowerize = function (str) {
- return typeof(str) === STR_TYPE ? str.toLowerCase() : UNDEF_TYPE;
+ lowerize = function (str, rgx) {
+ return typeof(str) === STR_TYPE ? str.toLowerCase().replace((rgx ? new RegExp(rgx, 'i') : EMPTY), EMPTY) : str;
},
majorize = function (version) {
return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined;
},
- sanitize = function (str, rgx) {
- return rgx ? lowerize(str).replace(rgx, EMPTY) : lowerize(str);
- },
trim = function (str, len) {
if (typeof(str) === STR_TYPE) {
str = str.replace(/^\s\s*/, EMPTY);
@@ -346,7 +345,7 @@
], [NAME, VERSION], [
/(cobalt)\/([\w\.]+)/i // Cobalt
- ], [NAME, [VERSION, /master.|lts./, ""]]
+ ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]]
],
cpu : [[
@@ -566,13 +565,13 @@
/\b(tm\d{3}\w+) b/i
], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
/\b(k88) b/i // ZTE K Series Tablet
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
+ ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [
/\b(nx\d{3}j) b/i // ZTE Nubia
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
+ ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [
/\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
+ ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [
/\b(zur\d{3}) b/i // Swiss ZUR Tablet
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
+ ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [
/\b((zeki)?tb.*\b) b/i // Zeki Tablets
], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
/\b([yr]\d{2}) b/i,
@@ -600,7 +599,7 @@
], [VENDOR, MODEL, [TYPE, MOBILE]], [
/(kin\.[onetw]{3})/i // Microsoft Kin
], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [
- /droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra
+ /droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra
], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [
/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i
], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [
@@ -624,7 +623,7 @@
/\(dtv[\);].+(aquos)/i,
/(aquos-tv[\w ]+)\)/i // Sharp
], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[
- /(bravia[\w ]+)( bui|\))/i // Sony
+ /(bravia[\w ]+)( bui|\))/i // Sony
], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
/(mitv-\w{5}) bui/i // Xiaomi
], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
@@ -641,11 +640,11 @@
///////////////////
/(ouya)/i, // Ouya
- /(nintendo) ([wids3utch]+)/i // Nintendo
+ /(nintendo) (\w+)/i // Nintendo
], [VENDOR, MODEL, [TYPE, CONSOLE]], [
/droid.+; (shield) bui/i // Nvidia
], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [
- /(playstation [345portablevi]+)/i // Playstation
+ /(playstation \w+)/i // Playstation
], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [
/\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox
], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
@@ -759,7 +758,7 @@
/(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
// Console
- /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation
+ /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation
/(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
// Other
@@ -787,73 +786,117 @@
// Constructor
////////////////
- function UAItem (propToString, propIs) {
- this.propToString = propToString;
- this.propIs = propIs[0];
- this.rgxIs = propIs[1];
- }
- UAItem.prototype.is = function (strCheck) {
- var is = false;
- for (var i in this.propIs) {
- if (sanitize(this[this.propIs[i]], this.rgxIs) == sanitize(strCheck, this.rgxIs)) {
- is = true;
- if (strCheck != UNDEF_TYPE) break;
- } else if (strCheck == UNDEF_TYPE && is) {
- is = !is;
- break;
- }
+ function UAItem () {}
+ UAItem.prototype.get = function (prop) {
+ if (!prop) {
+ return this.data;
}
- return is;
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
- UAItem.prototype.toString = function () {
- var str = '';
- for (var i in this.propToString) {
- if (typeof(this[this.propToString[i]]) !== UNDEF_TYPE) {
- str += (str ? ' ' : '') + this[this.propToString[i]];
- }
- }
- return str ? str : UNDEF_TYPE;
+ UAItem.prototype.parse = function (ua, rgxmap) {
+ rgxMapper.call(this.data, ua, rgxmap);
+ };
+ UAItem.prototype.set = function (prop, val) {
+ this.data[prop] = val;
+ };
+ UAItem.prototype.then = function (callback) {
+ return callback(this.data);
+ };
+
+ var createUAData = function (data) {
+ return (function () {
+ var propIs = data.propIs;
+ var ignoreIs = data.ignoreIs;
+ var propToStr = data.propToStr;
+ var UAData = function () {
+ for (var i in data.props) {
+ this[data.props[i]] = undefined;
+ }
+ };
+ UAData.prototype.is = function (strToCheck) {
+ var is = false;
+ for (var i in propIs) {
+ if (lowerize(this[propIs[i]], ignoreIs) === lowerize(strToCheck, ignoreIs)) {
+ is = true;
+ if (strToCheck != UNDEF_TYPE) break;
+ } else if (strToCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
+ }
+ }
+ return is;
+ };
+ UAData.prototype.toString = function () {
+ var str = EMPTY;
+ for (var i in propToStr) {
+ if (typeof(this[propToStr[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : EMPTY) + this[propToStr[i]];
+ }
+ }
+ return str ? str : UNDEF_TYPE;
+ };
+ return new UAData();
+ })(data);
};
function UABrowser () {
- this[NAME] = undefined;
- this[VERSION] = undefined;
- this[MAJOR] = undefined;
+ this.data = createUAData({
+ props : [NAME, VERSION, MAJOR],
+ propIs : [NAME],
+ ignoreIs : ' ?browser$',
+ propToStr : [NAME, VERSION]
+ });
}
- UABrowser.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?browser$/i]);
+ UABrowser.prototype = new UAItem();
function UACPU () {
- this[ARCHITECTURE] = undefined;
+ this.data = createUAData({
+ props : [ARCHITECTURE],
+ propIs : [ARCHITECTURE],
+ propToStr : [ARCHITECTURE]
+ });
}
- UACPU.prototype = new UAItem([ARCHITECTURE], [[ARCHITECTURE]]);
+ UACPU.prototype = new UAItem();
function UADevice () {
- this[VENDOR] = undefined;
- this[MODEL] = undefined;
- this[TYPE] = undefined;
+ this.data = createUAData({
+ props : [TYPE, MODEL, VENDOR],
+ propIs : [TYPE, MODEL, VENDOR],
+ propToStr : [VENDOR, MODEL]
+ });
}
- UADevice.prototype = new UAItem([VENDOR, MODEL], [[TYPE, MODEL, VENDOR]]);
+ UADevice.prototype = new UAItem();
function UAEngine () {
- this[NAME] = undefined;
- this[VERSION] = undefined;
+ this.data = createUAData({
+ props : [NAME, VERSION],
+ propIs : [NAME],
+ propToStr : [NAME, VERSION]
+ });
}
- UAEngine.prototype = new UAItem([NAME, VERSION], [[NAME]]);
+ UAEngine.prototype = new UAItem();
function UAOS () {
- this[NAME] = undefined;
- this[VERSION] = undefined;
+ this.data = createUAData({
+ props : [NAME, VERSION],
+ propIs : [NAME],
+ ignoreIs : ' ?os$',
+ propToStr : [NAME, VERSION]
+ });
}
- UAOS.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?os$/i]);
-
- function UAResult (uap) {
- this.ua = uap.getUA();
- this.browser = uap.getBrowser();
- this.cpu = uap.getCPU();
- this.device = uap.getDevice();
- this.engine = uap.getEngine();
- this.os = uap.getOS();
+ UAOS.prototype = new UAItem();
+
+ function UAResult () {
+ this.data = {
+ ua : '',
+ browser : undefined,
+ cpu : undefined,
+ device : undefined,
+ engine : undefined,
+ os : undefined
+ };
}
+ UAResult.prototype = new UAItem();
function UAParser (ua, extensions) {
@@ -861,7 +904,6 @@
extensions = ua;
ua = undefined;
}
-
if (!(this instanceof UAParser)) {
return new UAParser(ua, extensions).getResult();
}
@@ -874,49 +916,56 @@
// public methods
this.getBrowser = function () {
var _browser = new UABrowser();
- rgxMapper.call(_browser, _ua, _rgxmap.browser);
- _browser[MAJOR] = majorize(_browser[VERSION]);
+ _browser.parse(_ua, _rgxmap.browser);
+ _browser.set(MAJOR, majorize(_browser.get(VERSION)));
// Brave-specific detection
if (_navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) {
- _browser[NAME] = 'Brave';
+ _browser.set(NAME, 'Brave');
}
- return _browser;
+ return _browser.get();
};
this.getCPU = function () {
var _cpu = new UACPU();
- rgxMapper.call(_cpu, _ua, _rgxmap.cpu);
- return _cpu;
+ _cpu.parse(_ua, _rgxmap.cpu);
+ return _cpu.get();
};
this.getDevice = function () {
var _device = new UADevice();
- rgxMapper.call(_device, _ua, _rgxmap.device);
- if (!_device[TYPE] && _uach && _uach.mobile) {
- _device[TYPE] = MOBILE;
+ _device.parse(_ua, _rgxmap.device);
+ if (!_device.get(TYPE) && _uach && _uach.mobile) {
+ _device.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (_device[MODEL] == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {
- _device[MODEL] = 'iPad';
- _device[TYPE] = TABLET;
+ if (_device.get(NAME) == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {
+ _device.set(MODEL, 'iPad');
+ _device.set(TYPE, TABLET);
}
- return _device;
+ return _device.get();
};
this.getEngine = function () {
var _engine = new UAEngine();
- rgxMapper.call(_engine, _ua, _rgxmap.engine);
- return _engine;
+ _engine.parse(_ua, _rgxmap.engine);
+ return _engine.get();
};
this.getOS = function () {
var _os = new UAOS();
- rgxMapper.call(_os, _ua, _rgxmap.os);
- if (!_os[NAME] && _uach && _uach.platform != 'Unknown') {
- _os[NAME] = _uach.platform
+ _os.parse(_ua, _rgxmap.os);
+ if (!_os.get(NAME) && _uach && _uach.platform != 'Unknown') {
+ _os.set(NAME, _uach.platform
.replace(/chrome os/i, CHROMIUM_OS)
- .replace(/macos/i, MAC_OS); // backward compatibility
+ .replace(/macos/i, MAC_OS)); // backward compatibility
}
- return _os;
+ return _os.get();
};
this.getResult = function () {
- return new UAResult(this);
+ var _result = new UAResult();
+ _result.set('ua', _ua);
+ _result.set('browser', this.getBrowser());
+ _result.set('cpu', this.getCPU());
+ _result.set('device', this.getDevice());
+ _result.set('engine', this.getEngine());
+ _result.set('os', this.getOS());
+ return _result.get();
};
this.getUA = function () {
return _ua;
diff --git a/test/test.js b/test/test.js
index 622df973b..a906e2ce2 100644
--- a/test/test.js
+++ b/test/test.js
@@ -45,7 +45,7 @@ var methods = [
describe('UAParser()', function () {
var ua = 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6';
- assert.deepStrictEqual(UAParser(ua), new UAParser().setUA(ua).getResult());
+ assert.deepEqual(UAParser(ua), new UAParser().setUA(ua).getResult());
});
describe('UAParser() constructor does not throw with undefined ua argument', function () {
@@ -227,7 +227,9 @@ describe('is() utility method', function () {
assert.strictEqual(uap.getDevice().is("Apple"), true);
uap.setUA("");
+ assert.strictEqual(uap.getDevice().model, undefined);
assert.strictEqual(uap.getDevice().is("undefined"), true);
+ assert.strictEqual(uap.getDevice().is(undefined), true);
});
//it('Should accept arch equivalent name', function () {
From c6b4c867d54048def80a358b571041ac308cd496 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 28 Feb 2023 00:10:56 +0700
Subject: [PATCH 032/388] Sponsorship content revision
---
readme.md | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/readme.md b/readme.md
index fdeccfd4d..fd32a7fb7 100644
--- a/readme.md
+++ b/readme.md
@@ -26,11 +26,16 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
-
+
↗ @51degrees/ua-parser-js
-UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.
This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).
↗ Visit 51Degrees UAParser to get started.
+UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.
This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).
Visit ↗ 51Degrees UAParser to get started.
+
+
+
+
+On 6 March, we’ll be hosting a demonstration of the 51Degrees UAParser. Register your place for the webinar ↗ here .
From 9ee128ae2b7644176cd693904dcdf01fb8c84701 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 28 Feb 2023 12:25:57 +0700
Subject: [PATCH 033/388] Fix failing CI test, update funding & license year
---
.github/FUNDING.yml | 2 +-
license.md | 2 +-
package.json | 4 ++++
readme.md | 16 ++++++++--------
src/ua-parser.js | 15 +++++++--------
test/test.js | 2 +-
6 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 3ec50a0bb..b8b83265b 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,6 +1,6 @@
# These are supported funding model platforms
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+github: faisalman
patreon: # Replace with a single Patreon username
open_collective: ua-parser-js
ko_fi: # Replace with a single Ko-fi username
diff --git a/license.md b/license.md
index 01d840a46..9ba50e27f 100644
--- a/license.md
+++ b/license.md
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2012-2021 Faisal Salman <>
+Copyright (c) 2012-2023 Faisal Salman <>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/package.json b/package.json
index 7d9877589..37854dce2 100644
--- a/package.json
+++ b/package.json
@@ -197,6 +197,10 @@
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/faisalman"
}
]
}
diff --git a/readme.md b/readme.md
index fd32a7fb7..acf44a1e9 100644
--- a/readme.md
+++ b/readme.md
@@ -90,10 +90,10 @@ The methods are self explanatory, here's a small overview on all the available m
---
-* `getResult() : UAResult`
+* `getResult()`
* returns `{ ua: '', browser: UABrowser {}, cpu: UACPU {}, device: UADevice {}, engine: UAEngine {}, os: UAOS {} }`
-* `getBrowser() : UABrowser`
+* `getBrowser()`
* returns `{ name: '', version: '' }`
```sh
@@ -117,7 +117,7 @@ Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser...
# 'browser.version' determined dynamically
```
-* `getDevice() : UADevice`
+* `getDevice()`
* returns `{ model: '', type: '', vendor: '' }`
```sh
@@ -141,7 +141,7 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE
# 'device.model' determined dynamically
```
-* `getEngine() : UAEngine`
+* `getEngine()`
* returns `{ name: '', version: '' }`
```sh
@@ -152,7 +152,7 @@ NetSurf, Presto, Tasman, Trident, w3m, WebKit
# 'engine.version' determined dynamically
```
-* `getOS() : UAOS`
+* `getOS()`
* returns `{ name: '', version: '' }`
```sh
@@ -169,7 +169,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal
# 'os.version' determined dynamically
```
-* `getCPU() : UACPU`
+* `getCPU()`
* returns `{ architecture: '' }`
```sh
@@ -177,7 +177,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal
68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64]
```
-* `getUA() : string`
+* `getUA()`
* returns UA string of current instance
* `setUA(uastring)`
@@ -479,7 +479,7 @@ Made with [contributors-img](https://contrib.rocks).
MIT License
-Copyright (c) 2012-2021 Faisal Salman <>
+Copyright (c) 2012-2023 Faisal Salman <>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 188fbcbc9..a219a3877 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -1,6 +1,6 @@
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v1.1.34
- Copyright © 2012-2021 Faisal Salman
+ Copyright © 2012-2023 Faisal Salman
MIT License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
Supports browser & node.js environment.
@@ -802,8 +802,7 @@
UAItem.prototype.then = function (callback) {
return callback(this.data);
};
-
- var createUAData = function (data) {
+ UAItem.createUAData = function (data) {
return (function () {
var propIs = data.propIs;
var ignoreIs = data.ignoreIs;
@@ -840,7 +839,7 @@
};
function UABrowser () {
- this.data = createUAData({
+ this.data = UAItem.createUAData({
props : [NAME, VERSION, MAJOR],
propIs : [NAME],
ignoreIs : ' ?browser$',
@@ -850,7 +849,7 @@
UABrowser.prototype = new UAItem();
function UACPU () {
- this.data = createUAData({
+ this.data = UAItem.createUAData({
props : [ARCHITECTURE],
propIs : [ARCHITECTURE],
propToStr : [ARCHITECTURE]
@@ -859,7 +858,7 @@
UACPU.prototype = new UAItem();
function UADevice () {
- this.data = createUAData({
+ this.data = UAItem.createUAData({
props : [TYPE, MODEL, VENDOR],
propIs : [TYPE, MODEL, VENDOR],
propToStr : [VENDOR, MODEL]
@@ -868,7 +867,7 @@
UADevice.prototype = new UAItem();
function UAEngine () {
- this.data = createUAData({
+ this.data = UAItem.createUAData({
props : [NAME, VERSION],
propIs : [NAME],
propToStr : [NAME, VERSION]
@@ -877,7 +876,7 @@
UAEngine.prototype = new UAItem();
function UAOS () {
- this.data = createUAData({
+ this.data = UAItem.createUAData({
props : [NAME, VERSION],
propIs : [NAME],
ignoreIs : ' ?os$',
diff --git a/test/test.js b/test/test.js
index a906e2ce2..7b4e4a448 100644
--- a/test/test.js
+++ b/test/test.js
@@ -228,7 +228,7 @@ describe('is() utility method', function () {
uap.setUA("");
assert.strictEqual(uap.getDevice().model, undefined);
- assert.strictEqual(uap.getDevice().is("undefined"), true);
+ assert.strictEqual(uap.getDevice().is("undefined"), false);
assert.strictEqual(uap.getDevice().is(undefined), true);
});
From d99ff741f4d4d254e1e744a49202a858dd49ac18 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 28 Feb 2023 14:03:09 +0700
Subject: [PATCH 034/388] Add new feature: ability to pass req.headers object
directly into UAParser
---
readme.md | 11 ++--
src/ua-parser.js | 129 ++++++++++++++++++++++++++---------------------
test/test.js | 108 +++++++++++++++++++++++++++++++--------
3 files changed, 164 insertions(+), 84 deletions(-)
diff --git a/readme.md b/readme.md
index acf44a1e9..73bfa27db 100644
--- a/readme.md
+++ b/readme.md
@@ -53,7 +53,7 @@ Usually you can find the user agent in:
## Constructor
When you call `UAParser` with the `new` keyword `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string.
Like so:
-* `new UAParser([user-agent:string][,extensions:object])`
+* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
```js
let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs
console.log(parser); // {}
@@ -70,7 +70,7 @@ console.log(parserResults);
```
When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
-* `UAParser([user-agent:string][,extensions:object])`
+* `UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
* returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
## Methods
@@ -293,16 +293,17 @@ engine.toString(); // "Blink 28.0.1500.95"
If you want to detect something that's not currently provided by UAParser.js (eg: bots, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own.
-* `UAParser([uastring,] extensions)`
+* `UAParser([uastring,] extensions [,headers:object(since@1.1)])`
```js
// Example:
let myOwnListOfBrowsers = [
- [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION]
+ [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
];
let myParser = new UAParser({ browser: myOwnListOfBrowsers });
let myUA = 'Mozilla/5.0 MyBrowser/1.3';
-console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3"}
+console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3", major: "1", type : "bot"}
+console.log(myParser.getBrowser().is('bot')); // true
// Another example:
let myOwnListOfDevices = [
diff --git a/src/ua-parser.js b/src/ua-parser.js
index a219a3877..7a3dc7a5e 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -37,6 +37,7 @@
SMARTTV = 'smarttv',
WEARABLE = 'wearable',
EMBEDDED = 'embedded',
+ USER_AGENT = 'user-agent',
UA_MAX_LENGTH = 350;
var AMAZON = 'Amazon',
@@ -71,11 +72,7 @@
var extend = function (regexes, extensions) {
var mergedRegexes = {};
for (var i in regexes) {
- if (extensions[i] && extensions[i].length % 2 === 0) {
- mergedRegexes[i] = extensions[i].concat(regexes[i]);
- } else {
- mergedRegexes[i] = regexes[i];
- }
+ mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i];
}
return mergedRegexes;
},
@@ -89,6 +86,11 @@
has = function (str1, str2) {
return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
},
+ isExtensions = function (obj) {
+ for (var prop in obj) {
+ return /^(browser|cpu|device|engine|os)$/.test(prop);
+ }
+ },
lowerize = function (str, rgx) {
return typeof(str) === STR_TYPE ? str.toLowerCase().replace((rgx ? new RegExp(rgx, 'i') : EMPTY), EMPTY) : str;
},
@@ -803,84 +805,83 @@
return callback(this.data);
};
UAItem.createUAData = function (data) {
- return (function () {
- var propIs = data.propIs;
- var ignoreIs = data.ignoreIs;
- var propToStr = data.propToStr;
- var UAData = function () {
- for (var i in data.props) {
- this[data.props[i]] = undefined;
- }
- };
- UAData.prototype.is = function (strToCheck) {
- var is = false;
- for (var i in propIs) {
- if (lowerize(this[propIs[i]], ignoreIs) === lowerize(strToCheck, ignoreIs)) {
- is = true;
- if (strToCheck != UNDEF_TYPE) break;
- } else if (strToCheck == UNDEF_TYPE && is) {
- is = !is;
- break;
- }
+ var is_ignoreProps = data.is_ignoreProps,
+ is_ignoreRgx = data.is_ignoreRgx,
+ toString_props = data.toString_props;
+
+ var UAData = function () {
+ for (var i in data.init_props) {
+ this[data.init_props[i]] = undefined;
+ }
+ };
+ UAData.prototype.is = function (strToCheck) {
+ var is = false;
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
+ is = true;
+ if (strToCheck != UNDEF_TYPE) break;
+ } else if (strToCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
}
- return is;
- };
- UAData.prototype.toString = function () {
- var str = EMPTY;
- for (var i in propToStr) {
- if (typeof(this[propToStr[i]]) !== UNDEF_TYPE) {
- str += (str ? ' ' : EMPTY) + this[propToStr[i]];
- }
+ }
+ return is;
+ };
+ UAData.prototype.toString = function () {
+ var str = EMPTY;
+ for (var i in toString_props) {
+ if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : EMPTY) + this[toString_props[i]];
}
- return str ? str : UNDEF_TYPE;
- };
- return new UAData();
- })(data);
+ }
+ return str ? str : UNDEF_TYPE;
+ };
+ return new UAData();
};
function UABrowser () {
this.data = UAItem.createUAData({
- props : [NAME, VERSION, MAJOR],
- propIs : [NAME],
- ignoreIs : ' ?browser$',
- propToStr : [NAME, VERSION]
+ init_props : [NAME, VERSION, MAJOR],
+ is_ignoreProps : [VERSION, MAJOR],
+ is_ignoreRgx : ' ?browser$',
+ toString_props : [NAME, VERSION]
});
}
UABrowser.prototype = new UAItem();
function UACPU () {
this.data = UAItem.createUAData({
- props : [ARCHITECTURE],
- propIs : [ARCHITECTURE],
- propToStr : [ARCHITECTURE]
+ init_props : [ARCHITECTURE],
+ is_ignoreProps : [],
+ toString_props : [ARCHITECTURE]
});
}
UACPU.prototype = new UAItem();
function UADevice () {
this.data = UAItem.createUAData({
- props : [TYPE, MODEL, VENDOR],
- propIs : [TYPE, MODEL, VENDOR],
- propToStr : [VENDOR, MODEL]
+ init_props : [TYPE, MODEL, VENDOR],
+ is_ignoreProps : [],
+ toString_props : [VENDOR, MODEL]
});
}
UADevice.prototype = new UAItem();
function UAEngine () {
this.data = UAItem.createUAData({
- props : [NAME, VERSION],
- propIs : [NAME],
- propToStr : [NAME, VERSION]
+ init_props : [NAME, VERSION],
+ is_ignoreProps : [VERSION],
+ toString_props : [NAME, VERSION]
});
}
UAEngine.prototype = new UAItem();
function UAOS () {
this.data = UAItem.createUAData({
- props : [NAME, VERSION],
- propIs : [NAME],
- ignoreIs : ' ?os$',
- propToStr : [NAME, VERSION]
+ init_props : [NAME, VERSION],
+ is_ignoreProps : [VERSION],
+ is_ignoreRgx : ' ?os$',
+ toString_props : [NAME, VERSION]
});
}
UAOS.prototype = new UAItem();
@@ -897,18 +898,30 @@
}
UAResult.prototype = new UAItem();
- function UAParser (ua, extensions) {
+ function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) {
- extensions = ua;
+ if (isExtensions(ua)) {
+ if (typeof extensions === OBJ_TYPE) {
+ headers = extensions; // case UAParser(extensions, headers)
+ }
+ extensions = ua; // case UAParser(extensions)
+ } else {
+ headers = ua; // case UAParser(headers)
+ }
ua = undefined;
+ } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) {
+ headers = extensions; // case UAParser(ua, headers)
+ extensions = undefined;
}
+
if (!(this instanceof UAParser)) {
- return new UAParser(ua, extensions).getResult();
+ return new UAParser(ua, extensions, headers).getResult();
}
-
var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined;
- var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : EMPTY);
+
+ // _ua = user-supplied string || window.navigator.userAgent || user-agent header || empty
+ var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : (!ua && headers && headers[USER_AGENT] ? headers[USER_AGENT] : EMPTY));
var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined;
var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
diff --git a/test/test.js b/test/test.js
index 7b4e4a448..339f3c309 100644
--- a/test/test.js
+++ b/test/test.js
@@ -106,6 +106,25 @@ describe('Extending Regex', function () {
parser2.setUA(uaString);
assert.strictEqual(parser2.getBrowser().name, 'MyOwnBrowser');
assert.strictEqual(parser1.getBrowser().version, '1.3');
+
+ let myOwnListOfBrowsers = [
+ [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
+ ];
+ let myParser = new UAParser({ browser: myOwnListOfBrowsers });
+ let myUA = 'Mozilla/5.0 MyBrowser/1.3';
+ assert.deepEqual(myParser.setUA(myUA).getBrowser(), {name: "MyBrowser", version: "1.3", major: "1", type : "bot"});
+ assert.strictEqual(myParser.getBrowser().is('bot'), true);
+
+ let myOwnListOfDevices = [
+ [/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]],
+ [/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]]
+ ];
+ let myParser2 = new UAParser({
+ browser: myOwnListOfBrowsers,
+ device: myOwnListOfDevices
+ });
+ let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max';
+ assert.deepEqual(myParser2.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"});
});
describe('User-agent length', function () {
@@ -248,25 +267,72 @@ describe('is() utility method', function () {
});
describe('toString() utility method', function () {
- let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
- assert.strictEqual(uap.getBrowser().name, "IEMobile");
- assert.strictEqual(uap.getBrowser().version, "11.0");
- assert.strictEqual(uap.getBrowser().major, "11");
- assert.strictEqual(uap.getBrowser().toString(), "IEMobile 11.0");
-
- assert.strictEqual(uap.getCPU().architecture, "arm");
- assert.strictEqual(uap.getCPU().toString(), "arm");
-
- assert.strictEqual(uap.getDevice().vendor, "Nokia");
- assert.strictEqual(uap.getDevice().model, "Lumia 635");
- assert.strictEqual(uap.getDevice().type, "mobile");
- assert.strictEqual(uap.getDevice().toString(), "Nokia Lumia 635");
-
- assert.strictEqual(uap.getEngine().name, "Trident");
- assert.strictEqual(uap.getEngine().version, "7.0");
- assert.strictEqual(uap.getEngine().toString(), "Trident 7.0");
-
- assert.strictEqual(uap.getOS().name, "Windows Phone");
- assert.strictEqual(uap.getOS().version, "8.1");
- assert.strictEqual(uap.getOS().toString(), "Windows Phone 8.1");
+ it('Should return full name', function () {
+ let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537');
+ assert.strictEqual(uap.getBrowser().name, "IEMobile");
+ assert.strictEqual(uap.getBrowser().version, "11.0");
+ assert.strictEqual(uap.getBrowser().major, "11");
+ assert.strictEqual(uap.getBrowser().toString(), "IEMobile 11.0");
+
+ assert.strictEqual(uap.getCPU().architecture, "arm");
+ assert.strictEqual(uap.getCPU().toString(), "arm");
+
+ assert.strictEqual(uap.getDevice().vendor, "Nokia");
+ assert.strictEqual(uap.getDevice().model, "Lumia 635");
+ assert.strictEqual(uap.getDevice().type, "mobile");
+ assert.strictEqual(uap.getDevice().toString(), "Nokia Lumia 635");
+
+ assert.strictEqual(uap.getEngine().name, "Trident");
+ assert.strictEqual(uap.getEngine().version, "7.0");
+ assert.strictEqual(uap.getEngine().toString(), "Trident 7.0");
+
+ assert.strictEqual(uap.getOS().name, "Windows Phone");
+ assert.strictEqual(uap.getOS().version, "8.1");
+ assert.strictEqual(uap.getOS().toString(), "Windows Phone 8.1");
+ });
+});
+
+describe('Read user-agent data from req.headers', function () {
+ const ua = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)';
+ const ext = {
+ engine : [
+ [/(msie)/i], [[UAParser.ENGINE.NAME, 'Custom Browser 1']],
+ [/(edge)/i], [[UAParser.ENGINE.NAME, 'Custom Browser 2']]
+ ]
+ };
+ const req = {
+ headers : {
+ 'user-agent' : 'Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 Edge/12.0'
+ }
+ };
+
+ it('Can be called with UAParser(ua)', function () {
+ let engine = UAParser(ua).engine;
+ assert.strictEqual(engine.name, "Trident");
+ });
+
+ it('Can be called with UAParser(ua, extensions)', function () {
+ let engine = UAParser(ua, ext).engine;
+ assert.strictEqual(engine.name, "Custom Browser 1");
+ });
+
+ it('Can be called with UAParser(ua, extensions, headers)', function () {
+ let engine = UAParser(ua, ext, req.headers).engine;
+ assert.strictEqual(engine.name, "Custom Browser 1");
+ });
+
+ it('Can be called with UAParser(ua, headers)', function () {
+ let engine = UAParser(ua, req.headers).engine;
+ assert.strictEqual(engine.name, "Trident");
+ });
+
+ it('Can be called with UAParser(extensions, headers)', function () {
+ let engine = UAParser(ext, req.headers).engine;
+ assert.strictEqual(engine.name, "Custom Browser 2");
+ });
+
+ it('Can be called with UAParser(headers)', function () {
+ let engine = UAParser(req.headers).engine;
+ assert.strictEqual(engine.name, "EdgeHTML");
+ });
});
\ No newline at end of file
From 3c3c03ceeba9920437533dcf1d72276acac091a3 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 4 Mar 2023 22:33:13 +0700
Subject: [PATCH 035/388] Add new feature: read Client Hints data from HTTP
Headers #408 #566 #588
---
readme.md | 37 +++--
src/ua-parser.js | 382 ++++++++++++++++++++++++++++++-----------------
test/test.js | 64 ++++++++
3 files changed, 335 insertions(+), 148 deletions(-)
diff --git a/readme.md b/readme.md
index 73bfa27db..6b2146d73 100644
--- a/readme.md
+++ b/readme.md
@@ -12,7 +12,7 @@
# UAParser.js
-JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data with relatively small footprint (~17KB minified, ~6KB gzipped) that can be used either in browser (client-side) or node.js (server-side).
+JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data that can be used either in browser (client-side) or node.js (server-side).
* Author : Faisal Salman <>
* Demo : https://faisalman.github.io/ua-parser-js
@@ -26,24 +26,20 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
-
+
↗ @51degrees/ua-parser-js
UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.
This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).
Visit ↗ 51Degrees UAParser to get started.
-
-
-On 6 March, we’ll be hosting a demonstration of the 51Degrees UAParser. Register your place for the webinar ↗ here .
-
---
# Documentation
-### UAParser([user-agent:string][,extensions:object])
+### UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])
In The Browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in nodejs for the function to work.
Usually you can find the user agent in:
@@ -55,7 +51,7 @@ When you call `UAParser` with the `new` keyword `UAParser` will return a new ins
Like so:
* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
```js
-let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs
+let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs
console.log(parser); // {}
let parserResults = parser.getResult();
console.log(parserResults);
@@ -297,24 +293,30 @@ If you want to detect something that's not currently provided by UAParser.js (eg
```js
// Example:
-let myOwnListOfBrowsers = [
+const myOwnListOfBrowsers = [
[/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
];
+
+const myUA = 'Mozilla/5.0 MyBrowser/1.3';
+
let myParser = new UAParser({ browser: myOwnListOfBrowsers });
-let myUA = 'Mozilla/5.0 MyBrowser/1.3';
+
console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3", major: "1", type : "bot"}
console.log(myParser.getBrowser().is('bot')); // true
// Another example:
-let myOwnListOfDevices = [
+const myOwnListOfDevices = [
[/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]],
[/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]]
];
+
+const myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max';
+
let myParser2 = new UAParser({
browser: myOwnListOfBrowsers,
device: myOwnListOfDevices
});
-let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max';
+
console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}
```
@@ -408,6 +410,17 @@ var uap = require('ua-parser-js');
http.createServer(function (req, res) {
// get user-agent header
var ua = uap(req.headers['user-agent']);
+
+ /* // BEGIN since@1.1 - you can also pass client-hints data to UAParser
+
+ var getHighEntropyValues = 'Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness';
+ res.setHeader('Accept-CH', getHighEntropyValues);
+ res.setHeader('Critical-CH', getHighEntropyValues);
+
+ var ua = uap(req.headers);
+
+ // END since@1.1 */
+
// write the result as response
res.end(JSON.stringify(ua, null, ' '));
})
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 7a3dc7a5e..f9b4edce1 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -38,7 +38,15 @@
WEARABLE = 'wearable',
EMBEDDED = 'embedded',
USER_AGENT = 'user-agent',
- UA_MAX_LENGTH = 350;
+ UA_MAX_LENGTH = 350,
+ 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_MOBILE = CH_HEADER + '-mobile',
+ CH_HEADER_MODEL = CH_HEADER + '-model',
+ CH_HEADER_PLATFORM = CH_HEADER + '-platform',
+ CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version';
var AMAZON = 'Amazon',
APPLE = 'Apple',
@@ -63,7 +71,8 @@
ZTE = 'ZTE',
FACEBOOK = 'Facebook',
CHROMIUM_OS = 'Chromium OS',
- MAC_OS = 'Mac OS';
+ MAC_OS = 'Mac OS',
+ WINDOWS = 'Windows';
///////////
// Helper
@@ -110,6 +119,8 @@
var rgxMapper = function (ua, arrays) {
+ if(!ua || !arrays) return;
+
var i = 0, j, k, p, q, matches, match;
// loop through all regexes maps
@@ -718,7 +729,7 @@
/(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i
], [NAME, [VERSION, strMapper, windowsVersionMap]], [
/(win(?=3|9|n)|win 9x )([nt\d\.]+)/i
- ], [[NAME, 'Windows'], [VERSION, strMapper, windowsVersionMap]], [
+ ], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [
// iOS/macOS
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
@@ -788,115 +799,174 @@
// Constructor
////////////////
- function UAItem () {}
+ function UAItem (data) {
+ if (!data) return;
+ this.ua = data[0];
+ this.rgxMap = data[1];
+ this.data = (function (data) {
+ var is_ignoreProps = data[3],
+ is_ignoreRgx = data[4],
+ toString_props = data[5];
+
+ var UAData = function () {
+ for (var init_props in data[2]) {
+ this[data[2][init_props]] = undefined;
+ }
+ };
+ UAData.prototype.is = function (strToCheck) {
+ var is = false;
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
+ is = true;
+ if (strToCheck != UNDEF_TYPE) break;
+ } else if (strToCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
+ }
+ }
+ return is;
+ };
+ UAData.prototype.toString = function () {
+ var str = EMPTY;
+ for (var i in toString_props) {
+ if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : EMPTY) + this[toString_props[i]];
+ }
+ }
+ return str ? str : UNDEF_TYPE;
+ };
+ return new UAData();
+ })(data);
+ }
UAItem.prototype.get = function (prop) {
- if (!prop) {
- return this.data;
- }
+ if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
- UAItem.prototype.parse = function (ua, rgxmap) {
- rgxMapper.call(this.data, ua, rgxmap);
+ UAItem.prototype.parse = function () {
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
+ return this;
};
UAItem.prototype.set = function (prop, val) {
this.data[prop] = val;
- };
- UAItem.prototype.then = function (callback) {
- return callback(this.data);
- };
- UAItem.createUAData = function (data) {
- var is_ignoreProps = data.is_ignoreProps,
- is_ignoreRgx = data.is_ignoreRgx,
- toString_props = data.toString_props;
-
- var UAData = function () {
- for (var i in data.init_props) {
- this[data.init_props[i]] = undefined;
- }
- };
- UAData.prototype.is = function (strToCheck) {
- var is = false;
- for (var i in this) {
- if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
- is = true;
- if (strToCheck != UNDEF_TYPE) break;
- } else if (strToCheck == UNDEF_TYPE && is) {
- is = !is;
- break;
- }
- }
- return is;
- };
- UAData.prototype.toString = function () {
- var str = EMPTY;
- for (var i in toString_props) {
- if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
- str += (str ? ' ' : EMPTY) + this[toString_props[i]];
- }
- }
- return str ? str : UNDEF_TYPE;
- };
- return new UAData();
+ return this;
};
- function UABrowser () {
- this.data = UAItem.createUAData({
- init_props : [NAME, VERSION, MAJOR],
- is_ignoreProps : [VERSION, MAJOR],
- is_ignoreRgx : ' ?browser$',
- toString_props : [NAME, VERSION]
- });
+ function UABrowser (ua, browserMap) {
+ UAItem.call(this, [
+ ua,
+ browserMap,
+ [NAME, VERSION, MAJOR],
+ [VERSION, MAJOR],
+ ' ?browser$',
+ [NAME, VERSION]
+ ]);
}
UABrowser.prototype = new UAItem();
+ UABrowser.prototype.parse = function (uach) {
+ if (uach) {
+ var brands = uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER];
+ if (brands) {
+ brands = brands.replace(/\\?\"/g, EMPTY).split(', ');
+ for (var i in brands) {
+ var brand = brands[i].split(';v='),
+ brandName = brand[0],
+ brandVersion = brand[1];
+ if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) {
+ this.set(NAME, brandName.replace(GOOGLE+' ', EMPTY))
+ .set(VERSION, brandVersion)
+ .set(MAJOR, majorize(brandVersion));
+ }
+ }
+ }
+ }
+ if (!this.get(NAME)) UAItem.prototype.parse.call(this);
+ return this;
+ };
- function UACPU () {
- this.data = UAItem.createUAData({
- init_props : [ARCHITECTURE],
- is_ignoreProps : [],
- toString_props : [ARCHITECTURE]
- });
+ function UACPU (ua, cpuMap) {
+ UAItem.call(this, [
+ ua,
+ cpuMap,
+ [ARCHITECTURE],
+ [],
+ null,
+ [ARCHITECTURE]
+ ]);
}
UACPU.prototype = new UAItem();
+ UACPU.prototype.parse = function (uach) {
+ if (uach) {
+ var archName = uach[CH_HEADER_ARCH];
+ archName += (archName && uach[CH_HEADER_BITNESS] == '64') ? '64' : EMPTY;
+ rgxMapper.call(this.data, archName, this.rgxMap);
+ }
+ if (!this.get(ARCHITECTURE)) UAItem.prototype.parse.call(this);
+ return this;
+ };
- function UADevice () {
- this.data = UAItem.createUAData({
- init_props : [TYPE, MODEL, VENDOR],
- is_ignoreProps : [],
- toString_props : [VENDOR, MODEL]
- });
+ function UADevice (ua, deviceMap) {
+ UAItem.call(this, [
+ ua,
+ deviceMap,
+ [TYPE, MODEL, VENDOR],
+ [],
+ null,
+ [VENDOR, MODEL]
+ ]);
}
UADevice.prototype = new UAItem();
+ UADevice.prototype.parse = function (uach) {
+ if (uach) {
+ this.set(TYPE, uach[CH_HEADER_MOBILE] == '?1' ?
+ MOBILE :
+ this.get(TYPE))
+ .set(MODEL, uach[CH_HEADER_MODEL] ?
+ uach[CH_HEADER_MODEL].replace(/\"/g, EMPTY) :
+ this.get(MODEL));
+ }
+ if (!this.get(TYPE) && !this.get(MODEL)) UAItem.prototype.parse.call(this);
+ return this;
+ };
- function UAEngine () {
- this.data = UAItem.createUAData({
- init_props : [NAME, VERSION],
- is_ignoreProps : [VERSION],
- toString_props : [NAME, VERSION]
- });
+ function UAEngine (ua, engineMap) {
+ UAItem.call(this, [
+ ua,
+ engineMap,
+ [NAME, VERSION],
+ [VERSION],
+ null,
+ [NAME, VERSION]
+ ]);
}
UAEngine.prototype = new UAItem();
- function UAOS () {
- this.data = UAItem.createUAData({
- init_props : [NAME, VERSION],
- is_ignoreProps : [VERSION],
- is_ignoreRgx : ' ?os$',
- toString_props : [NAME, VERSION]
- });
+ function UAOS (ua, osMap) {
+ UAItem.call(this, [
+ ua,
+ osMap,
+ [NAME, VERSION],
+ [VERSION],
+ ' ?os$',
+ [NAME, VERSION]
+ ]);
}
UAOS.prototype = new UAItem();
-
- function UAResult () {
- this.data = {
- ua : '',
- browser : undefined,
- cpu : undefined,
- device : undefined,
- engine : undefined,
- os : undefined
- };
- }
- UAResult.prototype = new UAItem();
+ UAOS.prototype.parse = function (uach) {
+ if (uach) {
+ var osName = uach[CH_HEADER_PLATFORM] ? uach[CH_HEADER_PLATFORM].replace(/\"/g, EMPTY) : undefined;
+ var osVersion = uach[CH_HEADER_PLATFORM_VER] ? uach[CH_HEADER_PLATFORM_VER].replace(/\"/g, EMPTY) : undefined;
+ osVersion = (osName == WINDOWS) ? (majorize(osVersion) >= 13 ? '11' : '10') : osVersion;
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
+ }
+ if (!this.get(NAME)) UAItem.prototype.parse.call(this);
+ if (/(chrome |mac)os/.test(this.get(NAME))) {
+ this.set(NAME, this.get(NAME)
+ .replace(/chrome os/i, CHROMIUM_OS)
+ .replace(/macos/i, MAC_OS));
+ }
+ return this;
+ };
function UAParser (ua, extensions, headers) {
@@ -908,6 +978,7 @@
extensions = ua; // case UAParser(extensions)
} else {
headers = ua; // case UAParser(headers)
+ extensions = undefined;
}
ua = undefined;
} else if (typeof ua === STR_TYPE && !isExtensions(extensions)) {
@@ -918,75 +989,114 @@
if (!(this instanceof UAParser)) {
return new UAParser(ua, extensions, headers).getResult();
}
- var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined;
- // _ua = user-supplied string || window.navigator.userAgent || user-agent header || empty
- var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : (!ua && headers && headers[USER_AGENT] ? headers[USER_AGENT] : EMPTY));
- var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined;
- var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
+ var navigator = (typeof window !== UNDEF_TYPE && window.navigator) ?
+ window.navigator :
+ undefined,
+
+ userAgent = ua ||
+ ((navigator && navigator.userAgent) ?
+ navigator.userAgent :
+ (headers && headers[USER_AGENT] ?
+ headers[USER_AGENT] :
+ EMPTY)),
+
+ clientHints = (navigator && navigator.userAgentData) ?
+ navigator.userAgentData :
+ undefined,
+
+ regexMap = extensions ?
+ extend(regexes, extensions) :
+ regexes;
// public methods
this.getBrowser = function () {
- var _browser = new UABrowser();
- _browser.parse(_ua, _rgxmap.browser);
- _browser.set(MAJOR, majorize(_browser.get(VERSION)));
- // Brave-specific detection
- if (_navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) {
- _browser.set(NAME, 'Brave');
+ var browser = new UABrowser(userAgent, regexMap.browser);
+ if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) {
+ browser.parse(headers);
+ } else {
+ browser.parse();
+ // Brave-specific detection
+ if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) {
+ browser.set(NAME, 'Brave');
+ }
}
- return _browser.get();
+ return browser
+ .set(MAJOR, majorize(browser.get(VERSION)))
+ .get();
};
+
this.getCPU = function () {
- var _cpu = new UACPU();
- _cpu.parse(_ua, _rgxmap.cpu);
- return _cpu.get();
+ var cpu = new UACPU(userAgent, regexMap.cpu);
+ if (headers && headers[CH_HEADER_ARCH]) {
+ cpu.parse(headers);
+ } else {
+ cpu.parse();
+ }
+ return cpu.get();
};
+
this.getDevice = function () {
- var _device = new UADevice();
- _device.parse(_ua, _rgxmap.device);
- if (!_device.get(TYPE) && _uach && _uach.mobile) {
- _device.set(TYPE, MOBILE);
- }
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (_device.get(NAME) == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {
- _device.set(MODEL, 'iPad');
- _device.set(TYPE, TABLET);
+ var device = new UADevice(userAgent, regexMap.device);
+ if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) {
+ device.parse(headers);
+ } else {
+ device.parse();
+ if (!device.get(TYPE) && clientHints && clientHints.mobile) {
+ device.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (device.get(NAME) == 'Macintosh' && navigator && typeof navigator.standalone !== UNDEF_TYPE && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) {
+ device
+ .set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
+ }
}
- return _device.get();
+ return device.get();
};
+
this.getEngine = function () {
- var _engine = new UAEngine();
- _engine.parse(_ua, _rgxmap.engine);
- return _engine.get();
+ return new UAEngine(userAgent, regexMap.engine)
+ .parse()
+ .get();
};
+
this.getOS = function () {
- var _os = new UAOS();
- _os.parse(_ua, _rgxmap.os);
- if (!_os.get(NAME) && _uach && _uach.platform != 'Unknown') {
- _os.set(NAME, _uach.platform
- .replace(/chrome os/i, CHROMIUM_OS)
- .replace(/macos/i, MAC_OS)); // backward compatibility
+ var os = new UAOS(userAgent, regexMap.os);
+ if (headers && headers[CH_HEADER_PLATFORM]) {
+ os.parse(headers);
+ } else {
+ os.parse();
+ if (!os.get(NAME) && clientHints && clientHints.platform != 'Unknown') {
+ os.set(NAME, clientHints.platform
+ .replace(/chrome os/i, CHROMIUM_OS)
+ .replace(/macos/i, MAC_OS)); // backward compatibility
+ }
}
- return _os.get();
+ return os.get();
};
+
this.getResult = function () {
- var _result = new UAResult();
- _result.set('ua', _ua);
- _result.set('browser', this.getBrowser());
- _result.set('cpu', this.getCPU());
- _result.set('device', this.getDevice());
- _result.set('engine', this.getEngine());
- _result.set('os', this.getOS());
- return _result.get();
+ return {
+ 'ua' : userAgent,
+ 'browser' : this.getBrowser(),
+ 'cpu' : this.getCPU(),
+ 'device' : this.getDevice(),
+ 'engine' : this.getEngine(),
+ 'os' : this.getOS()
+ };
};
+
this.getUA = function () {
- return _ua;
+ return userAgent;
};
+
this.setUA = function (ua) {
- _ua = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
+ userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
return this;
};
- this.setUA(_ua);
+
+ this.setUA(userAgent);
return this;
}
diff --git a/test/test.js b/test/test.js
index 339f3c309..88c252296 100644
--- a/test/test.js
+++ b/test/test.js
@@ -335,4 +335,68 @@ describe('Read user-agent data from req.headers', function () {
let engine = UAParser(req.headers).engine;
assert.strictEqual(engine.name, "EdgeHTML");
});
+});
+
+describe('Map UA-CH headers', function () {
+
+ const headers = {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
+ 'sec-ch-ua-arch' : 'ARM',
+ 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Pixel 99',
+ 'sec-ch-ua-platform' : 'Windows',
+ 'sec-ch-ua-platform-version' : '13',
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+ };
+
+ const headers2 = {
+ 'sec-ch-ua-mobile' : '?1',
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+ };
+
+ let uap = UAParser(headers);
+ let browser = uap.browser;
+ let cpu = uap.cpu;
+ let device = uap.device;
+ let engine = uap.engine;
+ let os = uap.os;
+
+ it('Can read from client-hints headers', function () {
+
+ assert.strictEqual(browser.name, "Chrome");
+ assert.strictEqual(browser.version, "93.0.1.2");
+ assert.strictEqual(browser.major, "93");
+ assert.strictEqual(cpu.architecture, "arm64");
+ assert.strictEqual(device.type, "mobile");
+ assert.strictEqual(device.model, "Pixel 99");
+ assert.strictEqual(device.vendor, undefined);
+ assert.strictEqual(engine.name, 'Blink');
+ assert.strictEqual(engine.version, '110.0.0.0');
+ assert.strictEqual(os.name, "Windows");
+ assert.strictEqual(os.version, "11");
+ });
+
+ it('Can read from user-agent header', function () {
+
+ uap = UAParser(headers2);
+ browser = uap.browser;
+ cpu = uap.cpu;
+ device = uap.device;
+ engine = uap.engine;
+ os = uap.os;
+
+ assert.strictEqual(browser.name, "Chrome");
+ assert.strictEqual(browser.version, "110.0.0.0");
+ assert.strictEqual(browser.major, "110");
+ assert.strictEqual(cpu.architecture, "amd64");
+ assert.strictEqual(device.type, "mobile");
+ assert.strictEqual(device.model, undefined);
+ assert.strictEqual(device.vendor, undefined);
+ assert.strictEqual(engine.name, 'Blink');
+ assert.strictEqual(engine.version, '110.0.0.0');
+ assert.strictEqual(os.name, "Linux");
+ assert.strictEqual(os.version, "x86_64");
+ });
});
\ No newline at end of file
From 5672a2e15c30a0b0f9181b0b2c0c57e75c1a9068 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 9 Mar 2023 23:51:23 +0700
Subject: [PATCH 036/388] Expose UA-CH data in getResult()
---
src/ua-parser.js | 185 ++++++++++++++++++++++++++++-------------------
test/test.js | 1 +
2 files changed, 110 insertions(+), 76 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index f9b4edce1..d0a9d2be5 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -95,20 +95,34 @@
has = function (str1, str2) {
return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
},
+ initialize = 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;
+ },
isExtensions = function (obj) {
for (var prop in obj) {
return /^(browser|cpu|device|engine|os)$/.test(prop);
}
},
lowerize = function (str, rgx) {
- return typeof(str) === STR_TYPE ? str.toLowerCase().replace((rgx ? new RegExp(rgx, 'i') : EMPTY), EMPTY) : str;
+ return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str;
},
majorize = function (version) {
- return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined;
+ return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
+ },
+ strip = function (pattern, str) {
+ return str.replace(pattern, EMPTY);
},
trim = function (str, len) {
if (typeof(str) === STR_TYPE) {
- str = str.replace(/^\s\s*/, EMPTY);
+ str = strip(/^\s\s*/, str);
return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
}
};
@@ -799,20 +813,45 @@
// Constructor
////////////////
+ function UACHData (uach, isBrowser) {
+ uach = uach || {};
+ if (!isBrowser) {
+ initialize.call(this, ['brands', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness']);
+ if ((uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])) {
+ this.brands = [];
+ var tokens = strip(/\\?\"/g, (uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])).split(', ');
+ for (var i = 0; i < tokens.length; i++) {
+ var token = tokens[i].split(';v=');
+ this.brands[i] = { brand : token[0], version : token[1] };
+ }
+ }
+ this.mobile = uach[CH_HEADER_MOBILE] == '?1';
+ var setHeader = function (header) {
+ return header ? strip(/\"/g, header) : undefined;
+ };
+ this.model = setHeader(uach[CH_HEADER_MODEL]);
+ this.platform = setHeader(uach[CH_HEADER_PLATFORM]);
+ this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]);
+ this.architecture = setHeader(uach[CH_HEADER_ARCH]);
+ this.bitness = setHeader(uach[CH_HEADER_BITNESS]);
+ }
+ return this;
+ }
+
function UAItem (data) {
if (!data) return;
this.ua = data[0];
this.rgxMap = data[1];
+ this.uaCH = data[2];
this.data = (function (data) {
- var is_ignoreProps = data[3],
- is_ignoreRgx = data[4],
- toString_props = data[5];
+ var init_props = data[3],
+ is_ignoreProps = data[4],
+ is_ignoreRgx = data[5],
+ toString_props = data[6];
- var UAData = function () {
- for (var init_props in data[2]) {
- this[data[2][init_props]] = undefined;
- }
- };
+ function UAData () {
+ initialize.call(this, init_props);
+ }
UAData.prototype.is = function (strToCheck) {
var is = false;
for (var i in this) {
@@ -851,10 +890,11 @@
return this;
};
- function UABrowser (ua, browserMap) {
+ function UABrowser (ua, browserMap, uach) {
UAItem.call(this, [
ua,
browserMap,
+ uach,
[NAME, VERSION, MAJOR],
[VERSION, MAJOR],
' ?browser$',
@@ -862,31 +902,27 @@
]);
}
UABrowser.prototype = new UAItem();
- UABrowser.prototype.parse = function (uach) {
- if (uach) {
- var brands = uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER];
- if (brands) {
- brands = brands.replace(/\\?\"/g, EMPTY).split(', ');
- for (var i in brands) {
- var brand = brands[i].split(';v='),
- brandName = brand[0],
- brandVersion = brand[1];
- if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) {
- this.set(NAME, brandName.replace(GOOGLE+' ', EMPTY))
- .set(VERSION, brandVersion)
- .set(MAJOR, majorize(brandVersion));
- }
+ UABrowser.prototype.parseCH = function () {
+ var brands = this.uaCH.brands;
+ if (brands) {
+ for (var i in brands) {
+ var brandName = brands[i].brand,
+ brandVersion = brands[i].version;
+ if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) {
+ this.set(NAME, strip(GOOGLE+' ', brandName))
+ .set(VERSION, brandVersion)
+ .set(MAJOR, majorize(brandVersion));
}
}
}
- if (!this.get(NAME)) UAItem.prototype.parse.call(this);
return this;
};
- function UACPU (ua, cpuMap) {
+ function UACPU (ua, cpuMap, uach) {
UAItem.call(this, [
ua,
cpuMap,
+ uach,
[ARCHITECTURE],
[],
null,
@@ -894,20 +930,18 @@
]);
}
UACPU.prototype = new UAItem();
- UACPU.prototype.parse = function (uach) {
- if (uach) {
- var archName = uach[CH_HEADER_ARCH];
- archName += (archName && uach[CH_HEADER_BITNESS] == '64') ? '64' : EMPTY;
- rgxMapper.call(this.data, archName, this.rgxMap);
- }
- if (!this.get(ARCHITECTURE)) UAItem.prototype.parse.call(this);
+ UACPU.prototype.parseCH = function () {
+ var archName = this.uaCH.architecture;
+ archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY;
+ rgxMapper.call(this.data, archName, this.rgxMap);
return this;
};
- function UADevice (ua, deviceMap) {
+ function UADevice (ua, deviceMap, uach) {
UAItem.call(this, [
ua,
deviceMap,
+ uach,
[TYPE, MODEL, VENDOR],
[],
null,
@@ -915,16 +949,13 @@
]);
}
UADevice.prototype = new UAItem();
- UADevice.prototype.parse = function (uach) {
- if (uach) {
- this.set(TYPE, uach[CH_HEADER_MOBILE] == '?1' ?
- MOBILE :
- this.get(TYPE))
- .set(MODEL, uach[CH_HEADER_MODEL] ?
- uach[CH_HEADER_MODEL].replace(/\"/g, EMPTY) :
- this.get(MODEL));
+ UADevice.prototype.parseCH = function () {
+ if (this.uaCH.mobile) {
+ this.set(TYPE, MOBILE);
+ }
+ if (this.uaCH.model) {
+ this.set(MODEL, strip(/\"/g, this.uaCH.model));
}
- if (!this.get(TYPE) && !this.get(MODEL)) UAItem.prototype.parse.call(this);
return this;
};
@@ -932,6 +963,7 @@
UAItem.call(this, [
ua,
engineMap,
+ null,
[NAME, VERSION],
[VERSION],
null,
@@ -940,10 +972,11 @@
}
UAEngine.prototype = new UAItem();
- function UAOS (ua, osMap) {
+ function UAOS (ua, osMap, uach) {
UAItem.call(this, [
ua,
osMap,
+ uach,
[NAME, VERSION],
[VERSION],
' ?os$',
@@ -951,22 +984,15 @@
]);
}
UAOS.prototype = new UAItem();
- UAOS.prototype.parse = function (uach) {
- if (uach) {
- var osName = uach[CH_HEADER_PLATFORM] ? uach[CH_HEADER_PLATFORM].replace(/\"/g, EMPTY) : undefined;
- var osVersion = uach[CH_HEADER_PLATFORM_VER] ? uach[CH_HEADER_PLATFORM_VER].replace(/\"/g, EMPTY) : undefined;
- osVersion = (osName == WINDOWS) ? (majorize(osVersion) >= 13 ? '11' : '10') : osVersion;
- this.set(NAME, osName)
- .set(VERSION, osVersion);
- }
- if (!this.get(NAME)) UAItem.prototype.parse.call(this);
- if (/(chrome |mac)os/.test(this.get(NAME))) {
- this.set(NAME, this.get(NAME)
- .replace(/chrome os/i, CHROMIUM_OS)
- .replace(/macos/i, MAC_OS));
- }
+ UAOS.prototype.parseCH = function (uach) {
+ var osName = this.uaCH.platform;
+ var osVersion = this.uaCH.platformVersion;
+ osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
return this;
};
+
function UAParser (ua, extensions, headers) {
@@ -1000,8 +1026,10 @@
(headers && headers[USER_AGENT] ?
headers[USER_AGENT] :
EMPTY)),
+
+ clientHints = new UACHData(headers, false),
- clientHints = (navigator && navigator.userAgentData) ?
+ userAgentData = (navigator && navigator.userAgentData) ?
navigator.userAgentData :
undefined,
@@ -1011,10 +1039,11 @@
// public methods
this.getBrowser = function () {
- var browser = new UABrowser(userAgent, regexMap.browser);
+ var browser = new UABrowser(userAgent, regexMap.browser, clientHints);
if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) {
- browser.parse(headers);
- } else {
+ browser.parseCH();
+ }
+ if (!browser.get(NAME)) {
browser.parse();
// Brave-specific detection
if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) {
@@ -1027,22 +1056,24 @@
};
this.getCPU = function () {
- var cpu = new UACPU(userAgent, regexMap.cpu);
+ var cpu = new UACPU(userAgent, regexMap.cpu, clientHints);
if (headers && headers[CH_HEADER_ARCH]) {
- cpu.parse(headers);
- } else {
+ cpu.parseCH();
+ }
+ if (!cpu.get(ARCHITECTURE)) {
cpu.parse();
}
return cpu.get();
};
this.getDevice = function () {
- var device = new UADevice(userAgent, regexMap.device);
+ var device = new UADevice(userAgent, regexMap.device, clientHints);
if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) {
- device.parse(headers);
- } else {
+ device.parseCH();
+ }
+ if (!device.get(TYPE) || !device.get(MODEL)) {
device.parse();
- if (!device.get(TYPE) && clientHints && clientHints.mobile) {
+ if (!device.get(TYPE) && userAgentData && userAgentData.mobile) {
device.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
@@ -1062,13 +1093,14 @@
};
this.getOS = function () {
- var os = new UAOS(userAgent, regexMap.os);
+ var os = new UAOS(userAgent, regexMap.os, clientHints);
if (headers && headers[CH_HEADER_PLATFORM]) {
- os.parse(headers);
- } else {
+ os.parseCH();
+ }
+ if (!os.get(NAME)) {
os.parse();
- if (!os.get(NAME) && clientHints && clientHints.platform != 'Unknown') {
- os.set(NAME, clientHints.platform
+ if (!os.get(NAME) && userAgentData && userAgentData.platform != 'Unknown') {
+ os.set(NAME, userAgentData.platform
.replace(/chrome os/i, CHROMIUM_OS)
.replace(/macos/i, MAC_OS)); // backward compatibility
}
@@ -1079,6 +1111,7 @@
this.getResult = function () {
return {
'ua' : userAgent,
+ 'ua_ch' : clientHints,
'browser' : this.getBrowser(),
'cpu' : this.getCPU(),
'device' : this.getDevice(),
diff --git a/test/test.js b/test/test.js
index 88c252296..677e02b90 100644
--- a/test/test.js
+++ b/test/test.js
@@ -82,6 +82,7 @@ describe('Returns', function () {
assert.deepEqual(new UAParser('').getResult(),
{
ua : '',
+ ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined },
browser: { name: undefined, version: undefined, major: undefined },
cpu: { architecture: undefined },
device: { vendor: undefined, model: undefined, type: undefined },
From aff5a209f8840b4465e6a698acebddcfa02b5005 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 11 Mar 2023 02:08:15 +0700
Subject: [PATCH 037/388] Add new method: withClientHints() #408 #566 #588
---
readme.md | 65 ++++++++--
src/ua-parser.js | 312 +++++++++++++++++++++++++++--------------------
test/test.js | 10 +-
3 files changed, 240 insertions(+), 147 deletions(-)
diff --git a/readme.md b/readme.md
index 6b2146d73..c37673527 100644
--- a/readme.md
+++ b/readme.md
@@ -12,7 +12,7 @@
# UAParser.js
-JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data that can be used either in browser (client-side) or node.js (server-side).
+JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client-Hints data that can be used either in browser (client-side) or node.js (server-side).
* Author : Faisal Salman <>
* Demo : https://faisalman.github.io/ua-parser-js
@@ -56,25 +56,28 @@ console.log(parser); // {}
let parserResults = parser.getResult();
console.log(parserResults);
/** {
- "ua": "",
- "browser": {},
- "engine": {},
- "os": {},
- "device": {},
- "cpu": {}
+ "ua" : "",
+ "browser" : {},
+ "engine" : {},
+ "os" : {},
+ "device" : {},
+ "cpu" : {}
+
+ // since@1.1
+ ,"ua_ch" : {}
} */
```
When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
* `UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
- * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
+ * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`
## Methods
#### Methods table
The methods are self explanatory, here's a small overview on all the available methods:
* `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os:
-`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`.
+`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`.
* `getBrowser()` - returns the browser name and version.
* `getDevice()` - returns the device model, type, vendor.
@@ -87,7 +90,7 @@ The methods are self explanatory, here's a small overview on all the available m
---
* `getResult()`
- * returns `{ ua: '', browser: UABrowser {}, cpu: UACPU {}, device: UADevice {}, engine: UAEngine {}, os: UAOS {} }`
+ * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`
* `getBrowser()`
* returns `{ name: '', version: '' }`
@@ -180,7 +183,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal
* set UA string to be parsed
* returns current instance
-#### * is() utility `since@1.1`
+#### * `is()` utility `since@1.1`
```js
// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive)
@@ -244,7 +247,7 @@ let engine = uap.getEngine();
engine.is("Blink"); // true
```
-#### * toString() utility `since@1.1`
+#### * `toString()` utility `since@1.1`
```js
// Retrieve full-name values as a string
@@ -285,9 +288,33 @@ engine.version; // "28.0.1500.95"
engine.toString(); // "Blink 28.0.1500.95"
```
+#### * `withClientHints()` `since@1.1`
+
+Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`.
+
+```js
+(async function () {
+ let ua = new UAParser();
+
+ // get browser data from user-agent only :
+ let browser = ua.getBrowser();
+ console.log('Using User-Agent: ', browser);
+
+ // get browser data from client-hints (with user-agent as fallback) :
+ browser = await ua.getBrowser().withClientHints();
+ console.log('Using Client-Hints: ', browser);
+
+ // alternatively :
+ ua.getBrowser().withClientHints().then(function (browser) {
+ console.log('Using Client-Hints: ', browser);
+ });
+})();
+```
+
+
## Extending Regex
-If you want to detect something that's not currently provided by UAParser.js (eg: bots, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own.
+If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own.
* `UAParser([uastring,] extensions [,headers:object(since@1.1)])`
@@ -359,6 +386,18 @@ console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "1
cpu: {
architecture: ""
}
+
+ // added since@1.1:
+ ,ua_ch: {
+ architecture: "",
+ brands: "",
+ bitness: "",
+ fullVersionList: "",
+ mobile: "",
+ model: "",
+ platform: "",
+ platformVersion: ""
+ }
}
*/
// Default result depends on current window.navigator.userAgent value
diff --git a/src/ua-parser.js b/src/ua-parser.js
index d0a9d2be5..0695044d2 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -46,7 +46,8 @@
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_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
+ CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'];
var AMAZON = 'Amazon',
APPLE = 'Apple',
@@ -73,6 +74,13 @@
CHROMIUM_OS = 'Chromium OS',
MAC_OS = 'Mac OS',
WINDOWS = 'Windows';
+
+ var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ?
+ window.navigator :
+ undefined,
+ NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ?
+ NAVIGATOR.userAgentData :
+ undefined;
///////////
// Helper
@@ -813,19 +821,23 @@
// Constructor
////////////////
- function UACHData (uach, isBrowser) {
+ function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
- if (!isBrowser) {
- initialize.call(this, ['brands', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness']);
- if ((uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])) {
- this.brands = [];
- var tokens = strip(/\\?\"/g, (uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])).split(', ');
+ initialize.call(this, CH_ALL_VALUES);
+ if (isHTTP_UACH) {
+ var toArray = function (header) {
+ if (!header) return undefined;
+ var arr = [];
+ var tokens = strip(/\\?\"/g, header).split(', ');
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i].split(';v=');
- this.brands[i] = { brand : token[0], version : token[1] };
+ arr[i] = { brand : token[0], version : token[1] };
}
- }
- this.mobile = uach[CH_HEADER_MOBILE] == '?1';
+ return arr;
+ };
+ this.brands = toArray(uach[CH_HEADER]);
+ this.fullVersionList = toArray(uach[CH_HEADER_FULL_VER_LIST]);
+ this.mobile = /\?1/.test(uach[CH_HEADER_MOBILE]);
var setHeader = function (header) {
return header ? strip(/\"/g, header) : undefined;
};
@@ -834,25 +846,32 @@
this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]);
this.architecture = setHeader(uach[CH_HEADER_ARCH]);
this.bitness = setHeader(uach[CH_HEADER_BITNESS]);
+ } else {
+ for (var prop in uach) {
+ this[prop] = uach[prop];
+ }
}
return this;
}
- function UAItem (data) {
+ function UAParserItem (data) {
if (!data) return;
this.ua = data[0];
- this.rgxMap = data[1];
- this.uaCH = data[2];
+ this.uaCH = data[1];
+ this.rgxMap = data[3];
this.data = (function (data) {
- var init_props = data[3],
- is_ignoreProps = data[4],
- is_ignoreRgx = data[5],
- toString_props = data[6];
-
- function UAData () {
+ var ua = data[0],
+ itemType = data[2],
+ rgxMap = data[3],
+ init_props = data[4],
+ is_ignoreProps = data[5],
+ is_ignoreRgx = data[6],
+ toString_props = data[7];
+
+ function UAParserData () {
initialize.call(this, init_props);
}
- UAData.prototype.is = function (strToCheck) {
+ UAParserData.prototype.is = function (strToCheck) {
var is = false;
for (var i in this) {
if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
@@ -865,7 +884,7 @@
}
return is;
};
- UAData.prototype.toString = function () {
+ UAParserData.prototype.toString = function () {
var str = EMPTY;
for (var i in toString_props) {
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
@@ -874,36 +893,83 @@
}
return str ? str : UNDEF_TYPE;
};
- return new UAData();
+ UAParserData.prototype.withClientHints = function () {
+ if (!NAVIGATOR_UADATA) return;
+ return NAVIGATOR_UADATA
+ .getHighEntropyValues(CH_ALL_VALUES)
+ .then(function (res) {
+
+ var JS_UACH = new UAParserDataCH(res, false),
+ browser = new UAParserBrowser(ua, rgxMap, JS_UACH).get(),
+ cpu = new UAParserCPU(ua, rgxMap, JS_UACH).get(),
+ device = new UAParserDevice(ua, rgxMap, JS_UACH).get(),
+ engine = new UAParserEngine(ua, rgxMap).get(),
+ os = new UAParserOS(ua, rgxMap, JS_UACH).get();
+
+ switch (itemType) {
+ case 'browser':
+ return browser;
+ case 'cpu':
+ return cpu;
+ case 'device':
+ return device;
+ case 'engine':
+ return engine;
+ case 'os':
+ return os;
+ default :
+ return {
+ 'ua' : ua,
+ 'ua_ch' : JS_UACH,
+ 'browser' : browser,
+ 'cpu' : cpu,
+ 'device' : device,
+ 'engine' : engine,
+ 'os' : os
+ };
+ }
+ });
+ };
+ return new UAParserData();
})(data);
}
- UAItem.prototype.get = function (prop) {
+ UAParserItem.prototype.get = function (prop) {
if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
- UAItem.prototype.parse = function () {
+ UAParserItem.prototype.parse = function () {
rgxMapper.call(this.data, this.ua, this.rgxMap);
return this;
};
- UAItem.prototype.set = function (prop, val) {
+ UAParserItem.prototype.set = function (prop, val) {
this.data[prop] = val;
return this;
};
- function UABrowser (ua, browserMap, uach) {
- UAItem.call(this, [
+ function UAParserBrowser (ua, browserMap, uach) {
+ UAParserItem.call(this, [
ua,
- browserMap,
uach,
+ 'browser',
+ browserMap,
[NAME, VERSION, MAJOR],
[VERSION, MAJOR],
' ?browser$',
[NAME, VERSION]
]);
+ this.parseCH();
+ if (!this.get(NAME)) {
+ this.parse();
+ // Brave-specific detection
+ if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ this.set(NAME, 'Brave');
+ }
+ }
+ this.set(MAJOR, majorize(this.get(VERSION)));
}
- UABrowser.prototype = new UAItem();
- UABrowser.prototype.parseCH = function () {
- var brands = this.uaCH.brands;
+ UAParserBrowser.prototype = new UAParserItem();
+ UAParserBrowser.prototype.parseCH = function () {
+ var brands = this.uaCH.fullVersionList || this.uaCH.brands;
if (brands) {
for (var i in brands) {
var brandName = brands[i].brand,
@@ -918,38 +984,58 @@
return this;
};
- function UACPU (ua, cpuMap, uach) {
- UAItem.call(this, [
+ function UAParserCPU (ua, cpuMap, uach) {
+ UAParserItem.call(this, [
ua,
- cpuMap,
uach,
+ 'cpu',
+ cpuMap,
[ARCHITECTURE],
[],
null,
[ARCHITECTURE]
]);
+ this.parseCH();
+ if (!this.get(ARCHITECTURE)) {
+ this.parse();
+ }
}
- UACPU.prototype = new UAItem();
- UACPU.prototype.parseCH = function () {
+ UAParserCPU.prototype = new UAParserItem();
+ UAParserCPU.prototype.parseCH = function () {
var archName = this.uaCH.architecture;
- archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY;
- rgxMapper.call(this.data, archName, this.rgxMap);
+ if (archName) {
+ archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY;
+ rgxMapper.call(this.data, archName, this.rgxMap);
+ }
return this;
};
- function UADevice (ua, deviceMap, uach) {
- UAItem.call(this, [
+ function UAParserDevice (ua, deviceMap, uach) {
+ UAParserItem.call(this, [
ua,
- deviceMap,
uach,
+ 'device',
+ deviceMap,
[TYPE, MODEL, VENDOR],
[],
null,
[VENDOR, MODEL]
]);
+ this.parseCH();
+ if (!this.get(TYPE) || !this.get(MODEL)) {
+ this.parse();
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.mobile) {
+ this.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ this.set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
+ }
+ }
}
- UADevice.prototype = new UAItem();
- UADevice.prototype.parseCH = function () {
+ UAParserDevice.prototype = new UAParserItem();
+ UAParserDevice.prototype.parseCH = function () {
if (this.uaCH.mobile) {
this.set(TYPE, MOBILE);
}
@@ -959,41 +1045,59 @@
return this;
};
- function UAEngine (ua, engineMap) {
- UAItem.call(this, [
+ function UAParserEngine (ua, engineMap) {
+ UAParserItem.call(this, [
ua,
- engineMap,
null,
+ 'engine',
+ engineMap,
[NAME, VERSION],
[VERSION],
null,
[NAME, VERSION]
]);
+ this.parse();
}
- UAEngine.prototype = new UAItem();
+ UAParserEngine.prototype = new UAParserItem();
- function UAOS (ua, osMap, uach) {
- UAItem.call(this, [
+ function UAParserOS (ua, osMap, uach) {
+ UAParserItem.call(this, [
ua,
- osMap,
uach,
+ 'os',
+ osMap,
[NAME, VERSION],
[VERSION],
' ?os$',
[NAME, VERSION]
]);
+ this.parseCH();
+ if (!this.get(NAME)) {
+ this.parse();
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform != 'Unknown') {
+ this.set(NAME, NAVIGATOR_UADATA.platform
+ .replace(/chrome os/i, CHROMIUM_OS)
+ .replace(/macos/i, MAC_OS)); // backward compatibility
+ }
+ }
}
- UAOS.prototype = new UAItem();
- UAOS.prototype.parseCH = function (uach) {
+ UAParserOS.prototype = new UAParserItem();
+ UAParserOS.prototype.parseCH = function () {
var osName = this.uaCH.platform;
- var osVersion = this.uaCH.platformVersion;
- osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
- this.set(NAME, osName)
- .set(VERSION, osVersion);
+ if(osName) {
+ var osVersion = this.uaCH.platformVersion;
+ osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
+ }
return this;
};
-
+ function UAParserResult (ua, uach) {
+ UAParserItem.call(this, [ua, uach]);
+ }
+ UAParserResult.prototype = new UAParserItem();
+
function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) {
@@ -1016,22 +1120,14 @@
return new UAParser(ua, extensions, headers).getResult();
}
- var navigator = (typeof window !== UNDEF_TYPE && window.navigator) ?
- window.navigator :
- undefined,
-
- userAgent = ua ||
- ((navigator && navigator.userAgent) ?
- navigator.userAgent :
+ var userAgent = ua ||
+ ((NAVIGATOR && NAVIGATOR.userAgent) ?
+ NAVIGATOR.userAgent :
(headers && headers[USER_AGENT] ?
headers[USER_AGENT] :
EMPTY)),
- clientHints = new UACHData(headers, false),
-
- userAgentData = (navigator && navigator.userAgentData) ?
- navigator.userAgentData :
- undefined,
+ HTTP_UACH = new UAParserDataCH(headers, true),
regexMap = extensions ?
extend(regexes, extensions) :
@@ -1039,85 +1135,35 @@
// public methods
this.getBrowser = function () {
- var browser = new UABrowser(userAgent, regexMap.browser, clientHints);
- if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) {
- browser.parseCH();
- }
- if (!browser.get(NAME)) {
- browser.parse();
- // Brave-specific detection
- if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) {
- browser.set(NAME, 'Brave');
- }
- }
- return browser
- .set(MAJOR, majorize(browser.get(VERSION)))
- .get();
+ return new UAParserBrowser(userAgent, regexMap.browser, HTTP_UACH).get();
};
this.getCPU = function () {
- var cpu = new UACPU(userAgent, regexMap.cpu, clientHints);
- if (headers && headers[CH_HEADER_ARCH]) {
- cpu.parseCH();
- }
- if (!cpu.get(ARCHITECTURE)) {
- cpu.parse();
- }
- return cpu.get();
+ return new UAParserCPU(userAgent, regexMap.cpu, HTTP_UACH).get();
};
this.getDevice = function () {
- var device = new UADevice(userAgent, regexMap.device, clientHints);
- if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) {
- device.parseCH();
- }
- if (!device.get(TYPE) || !device.get(MODEL)) {
- device.parse();
- if (!device.get(TYPE) && userAgentData && userAgentData.mobile) {
- device.set(TYPE, MOBILE);
- }
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (device.get(NAME) == 'Macintosh' && navigator && typeof navigator.standalone !== UNDEF_TYPE && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) {
- device
- .set(MODEL, 'iPad')
- .set(TYPE, TABLET);
- }
- }
- return device.get();
+ return new UAParserDevice(userAgent, regexMap.device, HTTP_UACH).get();
};
this.getEngine = function () {
- return new UAEngine(userAgent, regexMap.engine)
- .parse()
- .get();
+ return new UAParserEngine(userAgent, regexMap.engine).get();
};
this.getOS = function () {
- var os = new UAOS(userAgent, regexMap.os, clientHints);
- if (headers && headers[CH_HEADER_PLATFORM]) {
- os.parseCH();
- }
- if (!os.get(NAME)) {
- os.parse();
- if (!os.get(NAME) && userAgentData && userAgentData.platform != 'Unknown') {
- os.set(NAME, userAgentData.platform
- .replace(/chrome os/i, CHROMIUM_OS)
- .replace(/macos/i, MAC_OS)); // backward compatibility
- }
- }
- return os.get();
+ return new UAParserOS(userAgent, regexMap.os, HTTP_UACH).get();
};
this.getResult = function () {
- return {
- 'ua' : userAgent,
- 'ua_ch' : clientHints,
- 'browser' : this.getBrowser(),
- 'cpu' : this.getCPU(),
- 'device' : this.getDevice(),
- 'engine' : this.getEngine(),
- 'os' : this.getOS()
- };
+ return new UAParserResult(userAgent, HTTP_UACH)
+ .set('ua', userAgent)
+ .set('ua_ch', HTTP_UACH)
+ .set('browser', this.getBrowser())
+ .set('cpu', this.getCPU())
+ .set('device', this.getDevice())
+ .set('engine', this.getEngine())
+ .set('os', this.getOS())
+ .get();
};
this.getUA = function () {
diff --git a/test/test.js b/test/test.js
index 677e02b90..30f26d108 100644
--- a/test/test.js
+++ b/test/test.js
@@ -82,7 +82,7 @@ describe('Returns', function () {
assert.deepEqual(new UAParser('').getResult(),
{
ua : '',
- ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined },
+ 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 },
cpu: { architecture: undefined },
device: { vendor: undefined, model: undefined, type: undefined },
@@ -400,4 +400,12 @@ describe('Map UA-CH headers', function () {
assert.strictEqual(os.name, "Linux");
assert.strictEqual(os.version, "x86_64");
});
+});
+
+describe('Map UA-CH JS', function () {
+ it('Can read client hints from browser', async function () {
+ let ua = new UAParser();
+ let browser = await ua.getBrowser().withClientHints();
+ // TODO : create tests
+ });
});
\ No newline at end of file
From 60d3a2fbbc0670cdb8c3375d1eed47757604e3b6 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 12 Mar 2023 16:24:38 +0700
Subject: [PATCH 038/388] Create test for client hints in browser context
---
src/ua-parser.js | 92 ++++++++++++++++++++++++++----------------------
test/test.js | 62 +++++++++++++++++++++++++++++---
2 files changed, 106 insertions(+), 48 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 0695044d2..acc6fed07 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -47,22 +47,23 @@
CH_HEADER_MODEL = CH_HEADER + '-model',
CH_HEADER_PLATFORM = CH_HEADER + '-platform',
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
- CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'];
+ CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
+ UA_BROWSER = 'browser',
+ UA_CPU = 'cpu',
+ UA_DEVICE = 'device',
+ UA_ENGINE = 'engine',
+ UA_OS = 'os',
+ UA_RESULT = 'result';
var AMAZON = 'Amazon',
APPLE = 'Apple',
ASUS = 'ASUS',
BLACKBERRY = 'BlackBerry',
- BROWSER = 'Browser',
- CHROME = 'Chrome',
- EDGE = 'Edge',
- FIREFOX = 'Firefox',
GOOGLE = 'Google',
HUAWEI = 'Huawei',
LG = 'LG',
MICROSOFT = 'Microsoft',
MOTOROLA = 'Motorola',
- OPERA = 'Opera',
SAMSUNG = 'Samsung',
SHARP = 'Sharp',
SONY = 'Sony',
@@ -70,7 +71,14 @@
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
ZTE = 'ZTE',
+
+ BROWSER = 'Browser',
+ CHROME = 'Chrome',
+ EDGE = 'Edge',
+ FIREFOX = 'Firefox',
+ OPERA = 'Opera',
FACEBOOK = 'Facebook',
+
CHROMIUM_OS = 'Chromium OS',
MAC_OS = 'Mac OS',
WINDOWS = 'Windows';
@@ -385,12 +393,10 @@
cpu : [[
- /(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\)]/i // AMD64 (x64)
+ /\b(?:(amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64)
], [[ARCHITECTURE, 'amd64']], [
- /(ia32(?=;))/i // IA32 (quicktime)
- ], [[ARCHITECTURE, lowerize]], [
-
+ /(ia32(?=;))/i, // IA32 (quicktime)
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
], [[ARCHITECTURE, 'ia32']], [
@@ -824,6 +830,9 @@
function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
initialize.call(this, CH_ALL_VALUES);
+ var setVal = function (val) {
+ return typeof val === STR_TYPE ? strip(/\"/g, val) : val || undefined;
+ };
if (isHTTP_UACH) {
var toArray = function (header) {
if (!header) return undefined;
@@ -838,17 +847,14 @@
this.brands = toArray(uach[CH_HEADER]);
this.fullVersionList = toArray(uach[CH_HEADER_FULL_VER_LIST]);
this.mobile = /\?1/.test(uach[CH_HEADER_MOBILE]);
- var setHeader = function (header) {
- return header ? strip(/\"/g, header) : undefined;
- };
- this.model = setHeader(uach[CH_HEADER_MODEL]);
- this.platform = setHeader(uach[CH_HEADER_PLATFORM]);
- this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]);
- this.architecture = setHeader(uach[CH_HEADER_ARCH]);
- this.bitness = setHeader(uach[CH_HEADER_BITNESS]);
+ this.model = setVal(uach[CH_HEADER_MODEL]);
+ this.platform = setVal(uach[CH_HEADER_PLATFORM]);
+ this.platformVersion = setVal(uach[CH_HEADER_PLATFORM_VER]);
+ this.architecture = setVal(uach[CH_HEADER_ARCH]);
+ this.bitness = setVal(uach[CH_HEADER_BITNESS]);
} else {
for (var prop in uach) {
- this[prop] = uach[prop];
+ if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = setVal(uach[prop]);
}
}
return this;
@@ -894,28 +900,28 @@
return str ? str : UNDEF_TYPE;
};
UAParserData.prototype.withClientHints = function () {
- if (!NAVIGATOR_UADATA) return;
+ if (!NAVIGATOR_UADATA) return this;
return NAVIGATOR_UADATA
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
var JS_UACH = new UAParserDataCH(res, false),
browser = new UAParserBrowser(ua, rgxMap, JS_UACH).get(),
- cpu = new UAParserCPU(ua, rgxMap, JS_UACH).get(),
- device = new UAParserDevice(ua, rgxMap, JS_UACH).get(),
- engine = new UAParserEngine(ua, rgxMap).get(),
- os = new UAParserOS(ua, rgxMap, JS_UACH).get();
+ cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap.cpu : rgxMap), JS_UACH).get(),
+ device = new UAParserDevice(ua, rgxMap, JS_UACH).get(),
+ engine = new UAParserEngine(ua, rgxMap).get(),
+ os = new UAParserOS(ua, rgxMap, JS_UACH).get();
switch (itemType) {
- case 'browser':
+ case UA_BROWSER:
return browser;
- case 'cpu':
+ case UA_CPU:
return cpu;
- case 'device':
+ case UA_DEVICE:
return device;
- case 'engine':
+ case UA_ENGINE:
return engine;
- case 'os':
+ case UA_OS:
return os;
default :
return {
@@ -950,7 +956,7 @@
UAParserItem.call(this, [
ua,
uach,
- 'browser',
+ UA_BROWSER,
browserMap,
[NAME, VERSION, MAJOR],
[VERSION, MAJOR],
@@ -988,7 +994,7 @@
UAParserItem.call(this, [
ua,
uach,
- 'cpu',
+ UA_CPU,
cpuMap,
[ARCHITECTURE],
[],
@@ -1014,7 +1020,7 @@
UAParserItem.call(this, [
ua,
uach,
- 'device',
+ UA_DEVICE,
deviceMap,
[TYPE, MODEL, VENDOR],
[],
@@ -1049,7 +1055,7 @@
UAParserItem.call(this, [
ua,
null,
- 'engine',
+ UA_ENGINE,
engineMap,
[NAME, VERSION],
[VERSION],
@@ -1064,7 +1070,7 @@
UAParserItem.call(this, [
ua,
uach,
- 'os',
+ UA_OS,
osMap,
[NAME, VERSION],
[VERSION],
@@ -1074,7 +1080,7 @@
this.parseCH();
if (!this.get(NAME)) {
this.parse();
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform != 'Unknown') {
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform && NAVIGATOR_UADATA.platform != 'Unknown') {
this.set(NAME, NAVIGATOR_UADATA.platform
.replace(/chrome os/i, CHROMIUM_OS)
.replace(/macos/i, MAC_OS)); // backward compatibility
@@ -1093,8 +1099,8 @@
return this;
};
- function UAParserResult (ua, uach) {
- UAParserItem.call(this, [ua, uach]);
+ function UAParserResult (ua, resMap, uach) {
+ UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]);
}
UAParserResult.prototype = new UAParserItem();
@@ -1155,14 +1161,14 @@
};
this.getResult = function () {
- return new UAParserResult(userAgent, HTTP_UACH)
+ return new UAParserResult(userAgent, regexMap, HTTP_UACH)
.set('ua', userAgent)
.set('ua_ch', HTTP_UACH)
- .set('browser', this.getBrowser())
- .set('cpu', this.getCPU())
- .set('device', this.getDevice())
- .set('engine', this.getEngine())
- .set('os', this.getOS())
+ .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();
};
diff --git a/test/test.js b/test/test.js
index 30f26d108..329149741 100644
--- a/test/test.js
+++ b/test/test.js
@@ -402,10 +402,62 @@ describe('Map UA-CH headers', function () {
});
});
-describe('Map UA-CH JS', function () {
- it('Can read client hints from browser', async function () {
- let ua = new UAParser();
- let browser = await ua.getBrowser().withClientHints();
- // TODO : create tests
+describe('Map UA-CH JS', () => {
+
+ it('does not throw when using withClientHints() in non-supported environment', () => {
+ assert.doesNotThrow(() => {
+ new UAParser().getResult().withClientHints();
+ });
+ });
+
+ window = { navigator : { userAgent : '' , userAgentData : new NavigatorUAData() } };
+ function NavigatorUAData () {
+ this.mobile = false;
+ this.platform = '';
+ this.brands = [{ brand : 'A Chromium-based Browser', version : '1' }];
+ this.getHighEntropyValues = (values) => {
+ return new Promise((resolve) => {
+ const result = {
+ brands : [{ brand : 'A Chromium-based Browser', version : '1' }],
+ fullVersionList : [{ brand : 'A Chromium-based Browser', version : '1.2.3' }],
+ architecture : 'x86',
+ bitness : '64',
+ mobile : true,
+ model : 'Galaxy S3',
+ platform : 'Android',
+ platformVersion : '1000'
+ };
+ resolve(result);
+ });
+ }
+ };
+ delete require.cache[require.resolve('./../src/ua-parser')];
+ const UAParserWithWindow = require('./../src/ua-parser');
+
+ it('Can read client hints from browser', async () => {
+
+ let uap = new UAParserWithWindow()
+
+ let os = await uap.getOS().withClientHints();
+
+ assert.strictEqual(os.name, 'Android');
+ assert.strictEqual(os.is('Android'), true);
+ assert.strictEqual(os.toString(), 'Android 1000');
+
+ let result = await uap.getResult().withClientHints();
+
+ assert.strictEqual(result.browser.name, 'A Chromium-based Browser');
+ assert.strictEqual(result.browser.version, '1.2.3');
+ assert.strictEqual(result.cpu.architecture, 'amd64');
+ assert.strictEqual(result.os.name, 'Android');
+
+ let result_without_ch = uap.getResult();
+
+ assert.strictEqual(result_without_ch.browser.name, undefined);
+
+ uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
+ assert.strictEqual(uap.getOS().name, "Mac OS");
+
+ // TODO : create full tests
});
});
\ No newline at end of file
From f8dde65d54e92246a6ffe7f5a7af9262e87de137 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 14 Mar 2023 23:22:08 +0700
Subject: [PATCH 039/388] Only use user-agent data by default. Must explicitly
call `withClientHints()` to also use client-hints data
---
src/ua-parser.js | 163 ++++++++++++++++++++++++++---------------------
test/test.js | 146 +++++++++++++++++++++++++++++++++---------
2 files changed, 208 insertions(+), 101 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index acc6fed07..73e26f88e 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -39,6 +39,11 @@
EMBEDDED = 'embedded',
USER_AGENT = 'user-agent',
UA_MAX_LENGTH = 350,
+ BRANDS = 'brands',
+ 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',
@@ -127,6 +132,16 @@
return /^(browser|cpu|device|engine|os)$/.test(prop);
}
},
+ itemListToArray = function (header) {
+ if (!header) return undefined;
+ var arr = [];
+ var tokens = strip(/\\?\"/g, header).split(', ');
+ for (var i = 0; i < tokens.length; i++) {
+ var token = tokens[i].split(';v=');
+ arr[i] = { brand : token[0], version : token[1] };
+ }
+ return arr;
+ },
lowerize = function (str, rgx) {
return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str;
},
@@ -136,6 +151,9 @@
strip = function (pattern, str) {
return str.replace(pattern, EMPTY);
},
+ stripQuotes = function (val) {
+ return typeof val === STR_TYPE ? strip(/\"/g, val) : val;
+ },
trim = function (str, len) {
if (typeof(str) === STR_TYPE) {
str = strip(/^\s\s*/, str);
@@ -830,31 +848,21 @@
function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
initialize.call(this, CH_ALL_VALUES);
- var setVal = function (val) {
- return typeof val === STR_TYPE ? strip(/\"/g, val) : val || undefined;
- };
if (isHTTP_UACH) {
- var toArray = function (header) {
- if (!header) return undefined;
- var arr = [];
- var tokens = strip(/\\?\"/g, header).split(', ');
- for (var i = 0; i < tokens.length; i++) {
- var token = tokens[i].split(';v=');
- arr[i] = { brand : token[0], version : token[1] };
- }
- return arr;
- };
- this.brands = toArray(uach[CH_HEADER]);
- this.fullVersionList = toArray(uach[CH_HEADER_FULL_VER_LIST]);
- this.mobile = /\?1/.test(uach[CH_HEADER_MOBILE]);
- this.model = setVal(uach[CH_HEADER_MODEL]);
- this.platform = setVal(uach[CH_HEADER_PLATFORM]);
- this.platformVersion = setVal(uach[CH_HEADER_PLATFORM_VER]);
- this.architecture = setVal(uach[CH_HEADER_ARCH]);
- this.bitness = setVal(uach[CH_HEADER_BITNESS]);
+ initialize.call(this, [
+ [BRANDS, itemListToArray(uach[CH_HEADER])],
+ [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
+ [BRANDS, itemListToArray(uach[CH_HEADER])],
+ [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
+ [MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
+ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
+ [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
+ [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
+ [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
+ ]);
} else {
for (var prop in uach) {
- if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = setVal(uach[prop]);
+ if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]);
}
}
return this;
@@ -867,6 +875,7 @@
this.rgxMap = data[3];
this.data = (function (data) {
var ua = data[0],
+ uaCH = data[1],
itemType = data[2],
rgxMap = data[3],
init_props = data[4],
@@ -900,17 +909,41 @@
return str ? str : UNDEF_TYPE;
};
UAParserData.prototype.withClientHints = function () {
- if (!NAVIGATOR_UADATA) return this;
+ if (!NAVIGATOR_UADATA) {
+ var HTTP_UACH = uaCH;
+ switch (itemType) {
+ case UA_BROWSER:
+ return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get();
+ case UA_CPU:
+ return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get();
+ case UA_DEVICE:
+ return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get();
+ case UA_ENGINE:
+ return new UAParserEngine(ua, rgxMap).get();
+ case UA_OS:
+ return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get();
+ default :
+ return {
+ 'ua' : ua,
+ 'ua_ch' : uaCH,
+ 'browser' : new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get(),
+ 'cpu' : new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get(),
+ 'device' : new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get(),
+ 'engine' : new UAParserEngine(ua, rgxMap[UA_ENGINE]).get(),
+ 'os' : new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get()
+ };
+ }
+ }
return NAVIGATOR_UADATA
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
var JS_UACH = new UAParserDataCH(res, false),
- browser = new UAParserBrowser(ua, rgxMap, JS_UACH).get(),
- cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap.cpu : rgxMap), JS_UACH).get(),
- device = new UAParserDevice(ua, rgxMap, JS_UACH).get(),
+ browser = new UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(),
+ cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap[UA_CPU] : rgxMap), JS_UACH).parseCH().get(),
+ device = new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(),
engine = new UAParserEngine(ua, rgxMap).get(),
- os = new UAParserOS(ua, rgxMap, JS_UACH).get();
+ os = new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get();
switch (itemType) {
case UA_BROWSER:
@@ -963,24 +996,21 @@
' ?browser$',
[NAME, VERSION]
]);
- this.parseCH();
- if (!this.get(NAME)) {
- this.parse();
- // Brave-specific detection
- if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
- this.set(NAME, 'Brave');
- }
+ this.parse();
+ // Brave-specific detection
+ if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ this.set(NAME, 'Brave');
}
this.set(MAJOR, majorize(this.get(VERSION)));
}
UAParserBrowser.prototype = new UAParserItem();
UAParserBrowser.prototype.parseCH = function () {
- var brands = this.uaCH.fullVersionList || this.uaCH.brands;
+ var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS];
if (brands) {
for (var i in brands) {
var brandName = brands[i].brand,
brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) {
+ if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
this.set(NAME, strip(GOOGLE+' ', brandName))
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
@@ -1001,16 +1031,13 @@
null,
[ARCHITECTURE]
]);
- this.parseCH();
- if (!this.get(ARCHITECTURE)) {
- this.parse();
- }
+ this.parse();
}
UAParserCPU.prototype = new UAParserItem();
UAParserCPU.prototype.parseCH = function () {
- var archName = this.uaCH.architecture;
+ var archName = this.uaCH[ARCHITECTURE];
if (archName) {
- archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY;
+ archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY;
rgxMapper.call(this.data, archName, this.rgxMap);
}
return this;
@@ -1027,26 +1054,23 @@
null,
[VENDOR, MODEL]
]);
- this.parseCH();
- if (!this.get(TYPE) || !this.get(MODEL)) {
- this.parse();
- if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.mobile) {
- this.set(TYPE, MOBILE);
- }
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
- this.set(MODEL, 'iPad')
- .set(TYPE, TABLET);
- }
+ this.parse();
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ this.set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
}
}
UAParserDevice.prototype = new UAParserItem();
UAParserDevice.prototype.parseCH = function () {
- if (this.uaCH.mobile) {
+ if (this.uaCH[MOBILE]) {
this.set(TYPE, MOBILE);
}
- if (this.uaCH.model) {
- this.set(MODEL, strip(/\"/g, this.uaCH.model));
+ if (this.uaCH[MODEL]) {
+ this.set(MODEL, this.uaCH[MODEL]);
}
return this;
};
@@ -1077,21 +1101,18 @@
' ?os$',
[NAME, VERSION]
]);
- this.parseCH();
- if (!this.get(NAME)) {
- this.parse();
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform && NAVIGATOR_UADATA.platform != 'Unknown') {
- this.set(NAME, NAVIGATOR_UADATA.platform
- .replace(/chrome os/i, CHROMIUM_OS)
- .replace(/macos/i, MAC_OS)); // backward compatibility
- }
+ this.parse();
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]
+ .replace(/chrome os/i, CHROMIUM_OS)
+ .replace(/macos/i, MAC_OS)); // backward compatibility
}
}
UAParserOS.prototype = new UAParserItem();
UAParserOS.prototype.parseCH = function () {
- var osName = this.uaCH.platform;
+ var osName = this.uaCH[PLATFORM];
if(osName) {
- var osVersion = this.uaCH.platformVersion;
+ var osVersion = this.uaCH[PLATFORMVER];
osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
this.set(NAME, osName)
.set(VERSION, osVersion);
@@ -1141,23 +1162,23 @@
// public methods
this.getBrowser = function () {
- return new UAParserBrowser(userAgent, regexMap.browser, HTTP_UACH).get();
+ return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get();
};
this.getCPU = function () {
- return new UAParserCPU(userAgent, regexMap.cpu, HTTP_UACH).get();
+ return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get();
};
this.getDevice = function () {
- return new UAParserDevice(userAgent, regexMap.device, HTTP_UACH).get();
+ return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get();
};
this.getEngine = function () {
- return new UAParserEngine(userAgent, regexMap.engine).get();
+ return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get();
};
this.getOS = function () {
- return new UAParserOS(userAgent, regexMap.os, HTTP_UACH).get();
+ return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get();
};
this.getResult = function () {
diff --git a/test/test.js b/test/test.js
index 329149741..aab97ad91 100644
--- a/test/test.js
+++ b/test/test.js
@@ -351,54 +351,134 @@ describe('Map UA-CH headers', function () {
'sec-ch-ua-platform-version' : '13',
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
};
-
- const headers2 = {
- 'sec-ch-ua-mobile' : '?1',
- 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+
+ let uap = UAParser(headers).withClientHints();
+ let browser = new UAParser(headers).getBrowser().withClientHints();
+ let cpu = new UAParser(headers).getCPU().withClientHints();
+ let device = new UAParser(headers).getDevice().withClientHints();
+ let engine = new UAParser(headers).getEngine().withClientHints();
+ let os = new UAParser(headers).getOS().withClientHints();
+
+ let ua_ch = {
+ "architecture": "ARM",
+ "bitness": "64",
+ "brands": [
+ {
+ "brand": "Chromium",
+ "version": "93"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99"
+ }
+ ],
+ "fullVersionList": [
+ {
+ "brand": "Chromium",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99.0.1.2"
+ }
+ ],
+ "mobile": true,
+ "model": "Pixel 99",
+ "platform": "Windows",
+ "platformVersion": "13"
};
- let uap = UAParser(headers);
- let browser = uap.browser;
- let cpu = uap.cpu;
- let device = uap.device;
- let engine = uap.engine;
- let os = uap.os;
-
- it('Can read from client-hints headers', function () {
+ it('Can read from client-hints headers using `withClientHints()`', function () {
+ assert.deepEqual(uap.ua_ch, ua_ch);
+ assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");
+ assert.strictEqual(uap.browser.name, "Chrome");
+ assert.strictEqual(uap.browser.version, "93.0.1.2");
+ assert.strictEqual(uap.browser.major, "93");
assert.strictEqual(browser.name, "Chrome");
assert.strictEqual(browser.version, "93.0.1.2");
assert.strictEqual(browser.major, "93");
+ assert.strictEqual(uap.cpu.architecture, "arm64");
assert.strictEqual(cpu.architecture, "arm64");
+ assert.strictEqual(uap.device.type, "mobile");
+ assert.strictEqual(uap.device.model, "Pixel 99");
+ assert.strictEqual(uap.device.vendor, undefined);
assert.strictEqual(device.type, "mobile");
assert.strictEqual(device.model, "Pixel 99");
assert.strictEqual(device.vendor, undefined);
+ assert.strictEqual(uap.engine.name, 'Blink');
+ assert.strictEqual(uap.engine.version, '110.0.0.0');
assert.strictEqual(engine.name, 'Blink');
assert.strictEqual(engine.version, '110.0.0.0');
+ assert.strictEqual(uap.os.name, "Windows");
+ assert.strictEqual(uap.os.version, "11");
assert.strictEqual(os.name, "Windows");
assert.strictEqual(os.version, "11");
});
- it('Can read from user-agent header', function () {
+ it('Only read from user-agent header when called without `withClientHints()`', function () {
+
+ uap = UAParser(headers);
+ browser = new UAParser(headers).getBrowser();
+ cpu = new UAParser(headers).getCPU();
+ device = new UAParser(headers).getDevice();
+ engine = new UAParser(headers).getEngine();
+ os = new UAParser(headers).getOS();
+
+ assert.deepEqual(uap.ua_ch, ua_ch);
+ assert.strictEqual(uap.browser.name, "Chrome");
+ assert.strictEqual(uap.browser.version, "110.0.0.0");
+ assert.strictEqual(uap.browser.major, "110");
+ assert.strictEqual(uap.cpu.architecture, "amd64");
+ assert.strictEqual(uap.device.type, undefined);
+ assert.strictEqual(uap.device.model, undefined);
+ assert.strictEqual(uap.device.vendor, undefined);
+ assert.strictEqual(uap.engine.name, 'Blink');
+ assert.strictEqual(uap.engine.version, '110.0.0.0');
+ assert.strictEqual(uap.os.name, "Linux");
+ assert.strictEqual(uap.os.version, "x86_64");
+ });
- uap = UAParser(headers2);
- browser = uap.browser;
- cpu = uap.cpu;
- device = uap.device;
- engine = uap.engine;
- os = uap.os;
+ it('Fallback to user-agent header when using `withClientHints()` but found no client hints-related headers', function () {
- assert.strictEqual(browser.name, "Chrome");
- assert.strictEqual(browser.version, "110.0.0.0");
- assert.strictEqual(browser.major, "110");
- assert.strictEqual(cpu.architecture, "amd64");
- assert.strictEqual(device.type, "mobile");
- assert.strictEqual(device.model, undefined);
- assert.strictEqual(device.vendor, undefined);
- assert.strictEqual(engine.name, 'Blink');
- assert.strictEqual(engine.version, '110.0.0.0');
- assert.strictEqual(os.name, "Linux");
- assert.strictEqual(os.version, "x86_64");
+ const headers2 = {
+ 'sec-ch-ua-mobile' : '?1',
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+ };
+
+ uap = UAParser(headers2).withClientHints();
+
+ ua_ch = {
+ "architecture": undefined,
+ "bitness": undefined,
+ "brands": undefined,
+ "fullVersionList": undefined,
+ "mobile": true,
+ "model": undefined,
+ "platform": undefined,
+ "platformVersion": undefined
+ };
+
+ assert.deepEqual(uap.ua_ch, ua_ch);
+ assert.strictEqual(uap.browser.name, "Chrome");
+ assert.strictEqual(uap.browser.version, "110.0.0.0");
+ assert.strictEqual(uap.browser.major, "110");
+ assert.strictEqual(uap.cpu.architecture, "amd64");
+ assert.strictEqual(uap.device.type, "mobile");
+ assert.strictEqual(uap.device.model, undefined);
+ assert.strictEqual(uap.device.vendor, undefined);
+ assert.strictEqual(uap.engine.name, 'Blink');
+ assert.strictEqual(uap.engine.version, '110.0.0.0');
+ assert.strictEqual(uap.os.name, "Linux");
+ assert.strictEqual(uap.os.version, "x86_64");
});
});
@@ -451,6 +531,12 @@ describe('Map UA-CH JS', () => {
assert.strictEqual(result.cpu.architecture, 'amd64');
assert.strictEqual(result.os.name, 'Android');
+ await uap.getDevice().withClientHints().then((device) => {
+ assert.strictEqual(device.type, 'mobile');
+ assert.strictEqual(device.vendor, undefined);
+ assert.strictEqual(device.model, 'Galaxy S3');
+ });
+
let result_without_ch = uap.getResult();
assert.strictEqual(result_without_ch.browser.name, undefined);
From c78346d3b4b6ead2a558d835f439eda6115d7747 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 15 Mar 2023 23:22:34 +0700
Subject: [PATCH 040/388] Returns `withClientHints()` as `Thenable` in nodejs /
non-client-hints browsers
---
readme.md | 25 +++++++++++--------
src/ua-parser.js | 65 ++++++++++++++++++++++++++----------------------
2 files changed, 50 insertions(+), 40 deletions(-)
diff --git a/readme.md b/readme.md
index c37673527..93f5c8159 100644
--- a/readme.md
+++ b/readme.md
@@ -33,6 +33,11 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.
This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).
Visit ↗ 51Degrees UAParser to get started.
+
+
+↗ Become a sponsor
+
+
@@ -41,13 +46,11 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
# Documentation
### UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])
-In The Browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in nodejs for the function to work.
-Usually you can find the user agent in:
-`request.headers["user-agent"]`.
+In the browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in' nodejs for the function to work. Usually you can find the user agent in: `request.headers["user-agent"]`.
## Constructor
-When you call `UAParser` with the `new` keyword `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string.
+When you call `UAParser` with the `new` keyword, `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string.
Like so:
* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
```js
@@ -183,7 +186,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal
* set UA string to be parsed
* returns current instance
-#### * `is()` utility `since@1.1`
+#### * `is():boolean` utility `since@1.1`
```js
// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive)
@@ -247,7 +250,7 @@ let engine = uap.getEngine();
engine.is("Blink"); // true
```
-#### * `toString()` utility `since@1.1`
+#### * `toString():string` utility `since@1.1`
```js
// Retrieve full-name values as a string
@@ -288,9 +291,9 @@ engine.version; // "28.0.1500.95"
engine.toString(); // "Blink 28.0.1500.95"
```
-#### * `withClientHints()` `since@1.1`
+#### * `withClientHints():Promise|Thenable` `since@1.1`
-Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`.
+Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`. In nodejs-environment / browser-environment with non-secure context or without client-hints support (basically anything that's not chromium-based) this will return the updated data as a `Thenable` (can be chained with `then()`).
```js
(async function () {
@@ -451,11 +454,13 @@ http.createServer(function (req, res) {
var ua = uap(req.headers['user-agent']);
/* // BEGIN since@1.1 - you can also pass client-hints data to UAParser
-
+
+ // note: only works in secure context (https:// or localhost or file://)
+
var getHighEntropyValues = 'Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness';
res.setHeader('Accept-CH', getHighEntropyValues);
res.setHeader('Critical-CH', getHighEntropyValues);
-
+
var ua = uap(req.headers);
// END since@1.1 */
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 73e26f88e..c017e8c7e 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -909,7 +909,10 @@
return str ? str : UNDEF_TYPE;
};
UAParserData.prototype.withClientHints = function () {
+
+ // nodejs / non-client-hints browsers
if (!NAVIGATOR_UADATA) {
+
var HTTP_UACH = uaCH;
switch (itemType) {
case UA_BROWSER:
@@ -923,52 +926,54 @@
case UA_OS:
return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get();
default :
- return {
- 'ua' : ua,
- 'ua_ch' : uaCH,
- 'browser' : new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get(),
- 'cpu' : new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get(),
- 'device' : new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get(),
- 'engine' : new UAParserEngine(ua, rgxMap[UA_ENGINE]).get(),
- 'os' : new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get()
- };
+ return new UAParserResult(ua, rgxMap, HTTP_UACH)
+ .set('ua', ua)
+ .set('ua_ch', uaCH)
+ .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get())
+ .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get())
+ .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get())
+ .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
+ .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get())
+ .get();
}
}
+
+ // browsers based on chromium 85+
return NAVIGATOR_UADATA
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
- var JS_UACH = new UAParserDataCH(res, false),
- browser = new UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(),
- cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap[UA_CPU] : rgxMap), JS_UACH).parseCH().get(),
- device = new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(),
- engine = new UAParserEngine(ua, rgxMap).get(),
- os = new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get();
-
+ var JS_UACH = new UAParserDataCH(res, false);
switch (itemType) {
case UA_BROWSER:
- return browser;
+ return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get();
case UA_CPU:
- return cpu;
+ return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get();
case UA_DEVICE:
- return device;
+ return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get();
case UA_ENGINE:
- return engine;
+ return new UAParserEngine(ua, rgxMap).get();
case UA_OS:
- return os;
+ return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get();
default :
- return {
- 'ua' : ua,
- 'ua_ch' : JS_UACH,
- 'browser' : browser,
- 'cpu' : cpu,
- 'device' : device,
- 'engine' : engine,
- 'os' : os
- };
+ return new UAParserResult(ua, rgxMap, JS_UACH)
+ .set('ua', ua)
+ .set('ua_ch', JS_UACH)
+ .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get())
+ .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get())
+ .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get())
+ .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
+ .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get())
+ .get();
}
});
};
+ if (!NAVIGATOR_UADATA) {
+ UAParserData.prototype.then = function (cb) {
+ cb(this);
+ return this;
+ };
+ }
return new UAParserData();
})(data);
}
From 41f8d76968128659624e599822239ac382968808 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 18 Mar 2023 10:38:03 +0700
Subject: [PATCH 041/388] Remove `is()` & `toString()` prototype from
`getResult()` data
---
src/ua-parser.js | 46 ++++++++++++++++++++++++----------------------
1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index c017e8c7e..42ec24f16 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -886,28 +886,6 @@
function UAParserData () {
initialize.call(this, init_props);
}
- UAParserData.prototype.is = function (strToCheck) {
- var is = false;
- for (var i in this) {
- if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
- is = true;
- if (strToCheck != UNDEF_TYPE) break;
- } else if (strToCheck == UNDEF_TYPE && is) {
- is = !is;
- break;
- }
- }
- return is;
- };
- UAParserData.prototype.toString = function () {
- var str = EMPTY;
- for (var i in toString_props) {
- if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
- str += (str ? ' ' : EMPTY) + this[toString_props[i]];
- }
- }
- return str ? str : UNDEF_TYPE;
- };
UAParserData.prototype.withClientHints = function () {
// nodejs / non-client-hints browsers
@@ -968,6 +946,30 @@
}
});
};
+ if(itemType != UA_RESULT) {
+ UAParserData.prototype.is = function (strToCheck) {
+ var is = false;
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
+ is = true;
+ if (strToCheck != UNDEF_TYPE) break;
+ } else if (strToCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
+ }
+ }
+ return is;
+ };
+ UAParserData.prototype.toString = function () {
+ var str = EMPTY;
+ for (var i in toString_props) {
+ if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : EMPTY) + this[toString_props[i]];
+ }
+ }
+ return str ? str : UNDEF_TYPE;
+ };
+ }
if (!NAVIGATOR_UADATA) {
UAParserData.prototype.then = function (cb) {
cb(this);
From 6821276669e299b6d23a595fb60f386569955f09 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 18 Mar 2023 10:41:54 +0700
Subject: [PATCH 042/388] Fix #218 #491 #517 #518 - introduce breaking changes:
"Mac OS" => "macOS", "Chromium OS" => "Chrome OS"
---
src/ua-parser.js | 10 +++-------
test/os-test.json | 12 ++++++------
test/test.js | 7 ++++---
3 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 42ec24f16..3f703533c 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -84,8 +84,6 @@
OPERA = 'Opera',
FACEBOOK = 'Facebook',
- CHROMIUM_OS = 'Chromium OS',
- MAC_OS = 'Mac OS',
WINDOWS = 'Windows';
var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ?
@@ -783,7 +781,7 @@
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i,
/(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS
- ], [[NAME, MAC_OS], [VERSION, /_/g, '.']], [
+ ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [
// Mobile OSes
/droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS
@@ -809,7 +807,7 @@
/crkey\/([\d\.]+)/i // Google Chromecast
], [VERSION, [NAME, CHROME+'cast']], [
/(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS
- ], [[NAME, CHROMIUM_OS], VERSION],[
+ ], [[NAME, "Chrome OS"], VERSION],[
// Smart TVs
/panasonic;(viera)/i, // Panasonic Viera
@@ -1110,9 +1108,7 @@
]);
this.parse();
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]
- .replace(/chrome os/i, CHROMIUM_OS)
- .replace(/macos/i, MAC_OS)); // backward compatibility
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
}
UAParserOS.prototype = new UAParserItem();
diff --git a/test/os-test.json b/test/os-test.json
index d97c7ffce..f720a1aa6 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -670,7 +670,7 @@
"ua" : "Mozilla/5.0 (X11; CrOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36",
"expect" :
{
- "name" : "Chromium OS",
+ "name" : "Chrome OS",
"version" : "undefined"
}
},
@@ -679,7 +679,7 @@
"ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"expect" :
{
- "name" : "Chromium OS",
+ "name" : "Chrome OS",
"version" : "10575.58.0"
}
},
@@ -796,7 +796,7 @@
"ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)",
"expect" :
{
- "name" : "Mac OS",
+ "name" : "macOS",
"version" : "undefined"
}
},
@@ -805,7 +805,7 @@
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0",
"expect" :
{
- "name" : "Mac OS",
+ "name" : "macOS",
"version" : "x.y"
}
},
@@ -814,7 +814,7 @@
"ua" : "Mozilla/5.0 (Macintosh; PPC Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0",
"expect" :
{
- "name" : "Mac OS",
+ "name" : "macOS",
"version" : "x.y"
}
},
@@ -823,7 +823,7 @@
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36",
"expect" :
{
- "name" : "Mac OS",
+ "name" : "macOS",
"version" : "10.6.8"
}
},
diff --git a/test/test.js b/test/test.js
index aab97ad91..39e948e1d 100644
--- a/test/test.js
+++ b/test/test.js
@@ -229,11 +229,12 @@ describe('is() utility method', function () {
it('Should get result after reassignment', function () {
uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
- assert.strictEqual(uap.getOS().name, "Mac OS");
+ assert.strictEqual(uap.getOS().name, "macOS");
assert.strictEqual(uap.getOS().is("Mac OS"), true);
- assert.strictEqual(uap.getOS().is("M ac"), false);
assert.strictEqual(uap.getOS().is("macOS"), true);
assert.strictEqual(uap.getOS().is("mac OS"), true);
+
+ assert.strictEqual(uap.getOS().is("M ac"), false);
assert.strictEqual(uap.getOS().is("M a c "), false);
assert.strictEqual(uap.getOS().is("Mac OS OS"), false);
assert.strictEqual(uap.getOS().is("Mac OS X"), false);
@@ -542,7 +543,7 @@ describe('Map UA-CH JS', () => {
assert.strictEqual(result_without_ch.browser.name, undefined);
uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
- assert.strictEqual(uap.getOS().name, "Mac OS");
+ assert.strictEqual(uap.getOS().name, "macOS");
// TODO : create full tests
});
From 31f94f3a51b2a04a7d4bb7bb8039ab51d178ca17 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 18 Mar 2023 17:04:43 +0700
Subject: [PATCH 043/388] Fix #441 #591 - Generate ESM version of main CJS file
at build time
---
dist/ua-parser.mjs | 1219 ++++++++++++++++++++++++++++++++++++++++++++
package.json | 10 +-
readme.md | 6 +
3 files changed, 1233 insertions(+), 2 deletions(-)
create mode 100644 dist/ua-parser.mjs
diff --git a/dist/ua-parser.mjs b/dist/ua-parser.mjs
new file mode 100644
index 000000000..7d3aafd1e
--- /dev/null
+++ b/dist/ua-parser.mjs
@@ -0,0 +1,1219 @@
+// Generated ESM version of UAParser.js
+// Source file: /src/ua-parser.js
+
+/////////////////////////////////////////////////////////////////////////////////
+/* UAParser.js v1.1.34
+ Copyright © 2012-2023 Faisal Salman
+ MIT 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
+ Source : https://github.com/faisalman/ua-parser-js */
+/////////////////////////////////////////////////////////////////////////////////
+
+const window = undefined;
+
+ //////////////
+ // Constants
+ /////////////
+
+
+ var LIBVERSION = '1.1.34',
+ 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 = 350,
+ BRANDS = 'brands',
+ 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_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', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
+ UA_BROWSER = 'browser',
+ UA_CPU = 'cpu',
+ UA_DEVICE = 'device',
+ UA_ENGINE = 'engine',
+ UA_OS = 'os',
+ UA_RESULT = 'result';
+
+ var AMAZON = 'Amazon',
+ APPLE = 'Apple',
+ ASUS = 'ASUS',
+ BLACKBERRY = 'BlackBerry',
+ GOOGLE = 'Google',
+ HUAWEI = 'Huawei',
+ LG = 'LG',
+ MICROSOFT = 'Microsoft',
+ MOTOROLA = 'Motorola',
+ SAMSUNG = 'Samsung',
+ SHARP = 'Sharp',
+ SONY = 'Sony',
+ SWISS = 'Swiss',
+ XIAOMI = 'Xiaomi',
+ ZEBRA = 'Zebra',
+ ZTE = 'ZTE',
+
+ BROWSER = 'Browser',
+ CHROME = 'Chrome',
+ EDGE = 'Edge',
+ FIREFOX = 'Firefox',
+ OPERA = 'Opera',
+ FACEBOOK = 'Facebook',
+
+ WINDOWS = 'Windows';
+
+ var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ?
+ window.navigator :
+ undefined,
+ NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ?
+ NAVIGATOR.userAgentData :
+ undefined;
+
+ ///////////
+ // 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];
+ }
+ return mergedRegexes;
+ },
+ enumerize = function (arr) {
+ var enums = {};
+ for (var i=0; i 0) {
+ if (q.length === 2) {
+ if (typeof q[1] == FUNC_TYPE) {
+ // assign modified match
+ this[q[0]] = q[1].call(this, match);
+ } else {
+ // assign given value, ignore regex match
+ this[q[0]] = q[1];
+ }
+ } else if (q.length === 3) {
+ // check whether function or regex
+ if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
+ // call function (usually string mapper)
+ this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
+ } else {
+ // sanitize match using given regex
+ 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) {
+ // check if current value is array
+ if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
+ for (var j = 0; j < map[i].length; j++) {
+ if (has(map[i][j], str)) {
+ return (i === UNKNOWN) ? undefined : i;
+ }
+ }
+ } else if (has(map[i], str)) {
+ return (i === UNKNOWN) ? undefined : i;
+ }
+ }
+ return str;
+ };
+
+ ///////////////
+ // String map
+ //////////////
+
+ // Safari < 3.0
+ var oldSafariMap = {
+ '1.0' : '/8',
+ '1.2' : '/1',
+ '1.3' : '/3',
+ '2.0' : '/412',
+ '2.0.2' : '/416',
+ '2.0.3' : '/417',
+ '2.0.4' : '/419',
+ '?' : '/'
+ },
+ windowsVersionMap = {
+ 'ME' : '4.90',
+ 'NT 3.11' : 'NT3.51',
+ 'NT 4.0' : 'NT4.0',
+ '2000' : 'NT 5.0',
+ 'XP' : ['NT 5.1', 'NT 5.2'],
+ 'Vista' : 'NT 6.0',
+ '7' : 'NT 6.1',
+ '8' : 'NT 6.2',
+ '8.1' : 'NT 6.3',
+ '10' : ['NT 6.4', 'NT 10.0'],
+ 'RT' : 'ARM'
+ },
+ archEquivalenceMap = {
+ 'amd64' : ['x86-64', 'x64'],
+ 'ia32' : ['x86']
+ };
+
+ //////////////
+ // Regex map
+ /////////////
+
+ var regexes = {
+
+ browser : [[
+
+ /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
+ ], [VERSION, [NAME, 'Chrome']], [
+ /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge
+ ], [VERSION, [NAME, 'Edge']], [
+
+ // Presto based
+ /(opera mini)\/([-\w\.]+)/i, // Opera Mini
+ /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet
+ /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i // Opera
+ ], [NAME, VERSION], [
+ /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0
+ ], [VERSION, [NAME, OPERA+' Mini']], [
+ /\bopr\/([\w\.]+)/i // Opera Webkit
+ ], [VERSION, [NAME, OPERA]], [
+
+ // Mixed
+ /(kindle)\/([\w\.]+)/i, // Kindle
+ /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
+ // Trident based
+ /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
+ /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser
+ /(?: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, aka ShouQ
+ /(weibo)__([\d\.]+)/i // Weibo
+ ], [NAME, VERSION], [
+ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
+ ], [VERSION, [NAME, 'UC'+BROWSER]], [
+ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
+ /\bqbcore\/([\w\.]+).+microm/i
+ ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [
+ /micromessenger\/([\w\.]+)/i // WeChat
+ ], [VERSION, [NAME, 'WeChat']], [
+ /konqueror\/([\w\.]+)/i // Konqueror
+ ], [VERSION, [NAME, 'Konqueror']], [
+ /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11
+ ], [VERSION, [NAME, 'IE']], [
+ /yabrowser\/([\w\.]+)/i // Yandex
+ ], [VERSION, [NAME, 'Yandex']], [
+ /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
+ ], [[NAME, /(.+)/, '$1 Secure '+BROWSER], VERSION], [
+ /\bfocus\/([\w\.]+)/i // Firefox Focus
+ ], [VERSION, [NAME, FIREFOX+' Focus']], [
+ /\bopt\/([\w\.]+)/i // Opera Touch
+ ], [VERSION, [NAME, OPERA+' Touch']], [
+ /coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser
+ ], [VERSION, [NAME, 'Coc Coc']], [
+ /dolfin\/([\w\.]+)/i // Dolphin
+ ], [VERSION, [NAME, 'Dolphin']], [
+ /coast\/([\w\.]+)/i // Opera Coast
+ ], [VERSION, [NAME, OPERA+' Coast']], [
+ /miuibrowser\/([\w\.]+)/i // MIUI Browser
+ ], [VERSION, [NAME, 'MIUI '+BROWSER]], [
+ /fxios\/([-\w\.]+)/i // Firefox for iOS
+ ], [VERSION, [NAME, FIREFOX]], [
+ /\bqihu|(qi?ho?o?|360)browser/i // 360
+ ], [[NAME, '360 '+BROWSER]], [
+ /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i
+ ], [[NAME, /(.+)/, '$1 '+BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser
+ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
+ ], [[NAME, /_/g, ' '], VERSION], [
+ /(electron)\/([\w\.]+) safari/i, // Electron-based App
+ /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
+ /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser
+ ], [NAME, VERSION], [
+ /(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser
+ /(lbbrowser)/i, // LieBao Browser
+ /\[(linkedin)app\]/i // LinkedIn App for iOS & Android
+ ], [NAME], [
+
+ // WebView
+ /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android
+ ], [[NAME, FACEBOOK], VERSION], [
+ /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App
+ /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
+ /safari (line)\/([\w\.]+)/i, // Line App for iOS
+ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
+ /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
+ ], [NAME, VERSION], [
+ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
+ ], [VERSION, [NAME, 'GSA']], [
+
+ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
+ ], [VERSION, [NAME, CHROME+' Headless']], [
+
+ / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
+ ], [[NAME, CHROME+' WebView'], VERSION], [
+
+ /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
+ ], [VERSION, [NAME, 'Android '+BROWSER]], [
+
+ /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia
+ ], [NAME, VERSION], [
+
+ /version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i // Mobile Safari
+ ], [VERSION, [NAME, 'Mobile Safari']], [
+ /version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile
+ ], [VERSION, NAME], [
+ /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
+ ], [NAME, [VERSION, strMapper, oldSafariMap]], [
+
+ /(webkit|khtml)\/([\w\.]+)/i
+ ], [NAME, VERSION], [
+
+ // Gecko based
+ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
+ ], [[NAME, 'Netscape'], 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
+ /(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], [
+
+ /(cobalt)\/([\w\.]+)/i // Cobalt
+ ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]]
+ ],
+
+ cpu : [[
+
+ /\b(?:(amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64)
+ ], [[ARCHITECTURE, 'amd64']], [
+
+ /(ia32(?=;))/i, // IA32 (quicktime)
+ /((?:i[346]|x)86)[;\)]/i // IA32 (x86)
+ ], [[ARCHITECTURE, 'ia32']], [
+
+ /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
+ ], [[ARCHITECTURE, 'arm64']], [
+
+ /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
+ ], [[ARCHITECTURE, 'armhf']], [
+
+ // PocketPC mistakenly identified as PowerPC
+ /windows (ce|mobile); ppc;/i
+ ], [[ARCHITECTURE, 'arm']], [
+
+ /((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i // PowerPC
+ ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
+
+ /(sun4\w)[;\)]/i // SPARC
+ ], [[ARCHITECTURE, 'sparc']], [
+
+ /((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i
+ // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
+ ], [[ARCHITECTURE, lowerize]]
+ ],
+
+ device : [[
+
+ //////////////////////////
+ // MOBILES & TABLETS
+ /////////////////////////
+
+ // Samsung
+ /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i
+ ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [
+ /\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,
+ /samsung[- ]([-\w]+)/i,
+ /sec-(sgh\w+)/i
+ ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
+
+ // Apple
+ /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone
+ ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
+ /\((ipad);[-\w\),; ]+apple/i, // iPad
+ /applecoremedia\/[\w\.]+ \((ipad)/i,
+ /\b(ipad)\d\d?,\d\d?[;\]].+ios/i
+ ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
+ /(macintosh);/i
+ ], [MODEL, [VENDOR, APPLE]], [
+
+ // Sharp
+ /\b(sh-?[altvz]?\d\d[a-ekm]?)/i
+ ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [
+
+ // Huawei
+ /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i
+ ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
+ /(?:huawei|honor)([-\w ]+)[;\)]/i,
+ /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i
+ ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
+
+ // Xiaomi
+ /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO
+ /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
+ /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
+ /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
+ /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi
+ ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
+ /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets
+ ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
+
+ // OPPO
+ /; (\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]], [
+
+ // Vivo
+ /vivo (\w+)(?: bui|\))/i,
+ /\b(v[12]\d{3}\w?[at])(?: bui|;)/i
+ ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [
+
+ // Realme
+ /\b(rmx[12]\d{3})(?: bui|;|\))/i
+ ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [
+
+ // Motorola
+ /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
+ /\bmot(?:orola)?[- ](\w*)/i,
+ /((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i
+ ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [
+ /\b(mz60\d|xoom[2 ]{0,2}) build\//i
+ ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [
+
+ // LG
+ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
+ ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [
+ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
+ /\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i,
+ /\blg-?([\d\w]+) bui/i
+ ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [
+
+ // Lenovo
+ /(ideatab[-\w ]+)/i,
+ /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i
+ ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [
+
+ // Nokia
+ /(?:maemo|nokia).*(n900|lumia \d+)/i,
+ /nokia[-_ ]?([-\w\.]*)/i
+ ], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [
+
+ // Google
+ /(pixel c)\b/i // Google Pixel C
+ ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [
+ /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel
+ ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [
+
+ // Sony
+ /droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i
+ ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [
+ /sony tablet [ps]/i,
+ /\b(?:sony)?sgp\w+(?: bui|\))/i
+ ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [
+
+ // OnePlus
+ / (kb2005|in20[12]5|be20[12][59])\b/i,
+ /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
+ ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [
+
+ // Amazon
+ /(alexa)webm/i,
+ /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk
+ /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD
+ ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [
+ /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone
+ ], [[MODEL, /(.+)/g, 'Fire Phone $1'], [VENDOR, AMAZON], [TYPE, MOBILE]], [
+
+ // BlackBerry
+ /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
+ ], [MODEL, VENDOR, [TYPE, TABLET]], [
+ /\b((?:bb[a-f]|st[hv])100-\d)/i,
+ /\(bb10; (\w+)/i // BlackBerry 10
+ ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
+
+ // Asus
+ /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i
+ ], [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], [
+ / (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i
+ ], [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], [
+
+ // HTC
+ /(nexus 9)/i // HTC Nexus 9
+ ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [
+ /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC
+
+ // ZTE
+ /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
+ /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
+ ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [
+
+ // Acer
+ /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i
+ ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [
+
+ // Meizu
+ /droid.+; (m[1-5] note) bui/i,
+ /\bmz-([-\w]{2,})/i
+ ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
+
+ // MIXED
+ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
+ // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
+ /(hp) ([\w ]+\w)/i, // HP iPAQ
+ /(asus)-?(\w+)/i, // Asus
+ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
+ /(lenovo)[-_ ]?([-\w]+)/i, // Lenovo
+ /(jolla)/i, // Jolla
+ /(oppo) ?([\w ]+) bui/i // OPPO
+ ], [VENDOR, MODEL, [TYPE, MOBILE]], [
+
+ /(kobo)\s(ereader|touch)/i, // Kobo
+ /(archos) (gamepad2?)/i, // Archos
+ /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
+ /(kindle)\/([\w\.]+)/i, // Kindle
+ /(nook)[\w ]+build\/(\w+)/i, // Nook
+ /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
+ /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
+ /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
+ /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
+ /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
+ ], [VENDOR, MODEL, [TYPE, TABLET]], [
+
+ /(surface duo)/i // Surface Duo
+ ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [
+ /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone
+ ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [
+ /(u304aa)/i // AT&T
+ ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
+ /\bsie-(\w*)/i // Siemens
+ ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
+ /\b(rct\w+) b/i // RCA Tablets
+ ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
+ /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
+ ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
+ /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
+ ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
+ /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
+ ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
+ /\b(tm\d{3}\w+) b/i
+ ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
+ /\b(k88) b/i // ZTE K Series Tablet
+ ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [
+ /\b(nx\d{3}j) b/i // ZTE Nubia
+ ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [
+ /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
+ ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [
+ /\b(zur\d{3}) b/i // Swiss ZUR Tablet
+ ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [
+ /\b((zeki)?tb.*\b) b/i // Zeki Tablets
+ ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
+ /\b([yr]\d{2}) b/i,
+ /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
+ ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [
+ /\b(ns-?\w{0,9}) b/i // Insignia Tablets
+ ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
+ /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
+ ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
+ /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
+ ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
+ /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
+ ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
+ /\b(ph-1) /i // Essential PH-1
+ ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
+ /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
+ ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
+ /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
+ ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
+ /\btu_(1491) b/i // Rotor Tablets
+ ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [
+ /(shield[\w ]+) b/i // Nvidia Shield Tablets
+ ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [
+ /(sprint) (\w+)/i // Sprint Phones
+ ], [VENDOR, MODEL, [TYPE, MOBILE]], [
+ /(kin\.[onetw]{3})/i // Microsoft Kin
+ ], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [
+ /droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra
+ ], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [
+ /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i
+ ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [
+
+ ///////////////////
+ // SMARTTVS
+ ///////////////////
+
+ /smart-tv.+(samsung)/i // Samsung
+ ], [VENDOR, [TYPE, SMARTTV]], [
+ /hbbtv.+maple;(\d+)/i
+ ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
+ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV
+ ], [[VENDOR, LG], [TYPE, SMARTTV]], [
+ /(apple) ?tv/i // Apple TV
+ ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
+ /crkey/i // Google Chromecast
+ ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
+ /droid.+aft(\w)( bui|\))/i // Fire TV
+ ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
+ /\(dtv[\);].+(aquos)/i,
+ /(aquos-tv[\w ]+)\)/i // Sharp
+ ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[
+ /(bravia[\w ]+)( bui|\))/i // Sony
+ ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
+ /(mitv-\w{5}) bui/i // Xiaomi
+ ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
+ /Hbbtv.*(technisat) (.*);/i // TechniSAT
+ ], [VENDOR, MODEL, [TYPE, SMARTTV]], [
+ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
+ /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices
+ ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
+ /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
+ ], [[TYPE, SMARTTV]], [
+
+ ///////////////////
+ // CONSOLES
+ ///////////////////
+
+ /(ouya)/i, // Ouya
+ /(nintendo) (\w+)/i // Nintendo
+ ], [VENDOR, MODEL, [TYPE, CONSOLE]], [
+ /droid.+; (shield) bui/i // Nvidia
+ ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [
+ /(playstation \w+)/i // Playstation
+ ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [
+ /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox
+ ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
+
+ ///////////////////
+ // WEARABLES
+ ///////////////////
+
+ /((pebble))app/i // Pebble
+ ], [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]], [
+
+ ///////////////////
+ // EMBEDDED
+ ///////////////////
+
+ /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla
+ ], [VENDOR, [TYPE, EMBEDDED]], [
+
+ ////////////////////
+ // MIXED (GENERIC)
+ ///////////////////
+
+ /droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors
+ ], [MODEL, [TYPE, MOBILE]], [
+ /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors
+ ], [MODEL, [TYPE, TABLET]], [
+ /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
+ ], [[TYPE, TABLET]], [
+ /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile
+ ], [[TYPE, MOBILE]], [
+ /(android[-\w\. ]{0,9});.+buil/i // Generic Android Device
+ ], [MODEL, [VENDOR, 'Generic']]
+ ],
+
+ engine : [[
+
+ /windows.+ edge\/([\w\.]+)/i // EdgeHTML
+ ], [VERSION, [NAME, EDGE+'HTML']], [
+
+ /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink
+ ], [VERSION, [NAME, 'Blink']], [
+
+ /(presto)\/([\w\.]+)/i, // Presto
+ /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
+ /ekioh(flow)\/([\w\.]+)/i, // Flow
+ /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
+ /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab
+ ], [NAME, VERSION], [
+
+ /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko
+ ], [VERSION, NAME]
+ ],
+
+ os : [[
+
+ // Windows
+ /microsoft (windows) (vista|xp)/i // Windows (iTunes)
+ ], [NAME, VERSION], [
+ /(windows) nt 6\.2; (arm)/i, // Windows RT
+ /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone
+ /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i
+ ], [NAME, [VERSION, strMapper, windowsVersionMap]], [
+ /(win(?=3|9|n)|win 9x )([nt\d\.]+)/i
+ ], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [
+
+ // iOS/macOS
+ /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
+ /cfnetwork\/.+darwin/i
+ ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
+ /(mac os x) ?([\w\. ]*)/i,
+ /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS
+ ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [
+
+ // Mobile OSes
+ /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS
+ ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS
+ /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i,
+ /(blackberry)\w*\/([\w\.]*)/i, // Blackberry
+ /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS
+ /\((series40);/i // Series 40
+ ], [NAME, VERSION], [
+ /\(bb(10);/i // BlackBerry 10
+ ], [VERSION, [NAME, BLACKBERRY]], [
+ /(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i // Symbian
+ ], [VERSION, [NAME, 'Symbian']], [
+ /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS
+ ], [VERSION, [NAME, FIREFOX+' OS']], [
+ /web0s;.+rt(tv)/i,
+ /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS
+ ], [VERSION, [NAME, 'webOS']], [
+ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS
+ ], [VERSION, [NAME, 'watchOS']], [
+
+ // Google Chromecast
+ /crkey\/([\d\.]+)/i // Google Chromecast
+ ], [VERSION, [NAME, CHROME+'cast']], [
+ /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS
+ ], [[NAME, "Chrome OS"], VERSION],[
+
+ // Smart TVs
+ /panasonic;(viera)/i, // Panasonic Viera
+ /(netrange)mmh/i, // Netrange
+ /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
+
+ // Console
+ /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation
+ /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
+
+ // Other
+ /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm
+ /(mint)[\/\(\) ]?(\w*)/i, // Mint
+ /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux
+ /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,
+ // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire
+ /(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux
+ /(gnu) ?([\w\.]*)/i, // GNU
+ /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly
+ /(haiku) (\w+)/i // Haiku
+ ], [NAME, VERSION], [
+ /(sunos) ?([\w\.\d]*)/i // Solaris
+ ], [[NAME, 'Solaris'], VERSION], [
+ /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
+ /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX
+ /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX
+ /(unix) ?([\w\.]*)/i // UNIX
+ ], [NAME, VERSION]
+ ]
+ };
+
+ /////////////////
+ // Constructor
+ ////////////////
+
+ function UAParserDataCH (uach, isHTTP_UACH) {
+ uach = uach || {};
+ initialize.call(this, CH_ALL_VALUES);
+ if (isHTTP_UACH) {
+ initialize.call(this, [
+ [BRANDS, itemListToArray(uach[CH_HEADER])],
+ [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
+ [BRANDS, itemListToArray(uach[CH_HEADER])],
+ [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
+ [MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
+ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
+ [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
+ [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
+ [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
+ ]);
+ } else {
+ for (var prop in uach) {
+ if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]);
+ }
+ }
+ return this;
+ }
+
+ function UAParserItem (data) {
+ if (!data) return;
+ this.ua = data[0];
+ this.uaCH = data[1];
+ this.rgxMap = data[3];
+ this.data = (function (data) {
+ var ua = data[0],
+ uaCH = data[1],
+ itemType = data[2],
+ rgxMap = data[3],
+ init_props = data[4],
+ is_ignoreProps = data[5],
+ is_ignoreRgx = data[6],
+ toString_props = data[7];
+
+ function UAParserData () {
+ initialize.call(this, init_props);
+ }
+ UAParserData.prototype.withClientHints = function () {
+
+ // nodejs / non-client-hints browsers
+ if (!NAVIGATOR_UADATA) {
+
+ var HTTP_UACH = uaCH;
+ switch (itemType) {
+ case UA_BROWSER:
+ return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get();
+ case UA_CPU:
+ return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get();
+ case UA_DEVICE:
+ return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get();
+ case UA_ENGINE:
+ return new UAParserEngine(ua, rgxMap).get();
+ case UA_OS:
+ return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get();
+ default :
+ return new UAParserResult(ua, rgxMap, HTTP_UACH)
+ .set('ua', ua)
+ .set('ua_ch', uaCH)
+ .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get())
+ .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get())
+ .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get())
+ .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
+ .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get())
+ .get();
+ }
+ }
+
+ // browsers based on chromium 85+
+ return NAVIGATOR_UADATA
+ .getHighEntropyValues(CH_ALL_VALUES)
+ .then(function (res) {
+
+ var JS_UACH = new UAParserDataCH(res, false);
+ switch (itemType) {
+ case UA_BROWSER:
+ return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get();
+ case UA_CPU:
+ return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get();
+ case UA_DEVICE:
+ return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get();
+ case UA_ENGINE:
+ return new UAParserEngine(ua, rgxMap).get();
+ case UA_OS:
+ return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get();
+ default :
+ return new UAParserResult(ua, rgxMap, JS_UACH)
+ .set('ua', ua)
+ .set('ua_ch', JS_UACH)
+ .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get())
+ .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get())
+ .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get())
+ .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
+ .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get())
+ .get();
+ }
+ });
+ };
+ if(itemType != UA_RESULT) {
+ UAParserData.prototype.is = function (strToCheck) {
+ var is = false;
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
+ is = true;
+ if (strToCheck != UNDEF_TYPE) break;
+ } else if (strToCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
+ }
+ }
+ return is;
+ };
+ UAParserData.prototype.toString = function () {
+ var str = EMPTY;
+ for (var i in toString_props) {
+ if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : EMPTY) + this[toString_props[i]];
+ }
+ }
+ return str ? str : UNDEF_TYPE;
+ };
+ }
+ if (!NAVIGATOR_UADATA) {
+ UAParserData.prototype.then = function (cb) {
+ cb(this);
+ return this;
+ };
+ }
+ return new UAParserData();
+ })(data);
+ }
+ UAParserItem.prototype.get = function (prop) {
+ if (!prop) return this.data;
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
+ };
+ UAParserItem.prototype.parse = function () {
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
+ return this;
+ };
+ UAParserItem.prototype.set = function (prop, val) {
+ this.data[prop] = val;
+ return this;
+ };
+
+ function UAParserBrowser (ua, browserMap, uach) {
+ UAParserItem.call(this, [
+ ua,
+ uach,
+ UA_BROWSER,
+ browserMap,
+ [NAME, VERSION, MAJOR],
+ [VERSION, MAJOR],
+ ' ?browser$',
+ [NAME, VERSION]
+ ]);
+ this.parse();
+ // Brave-specific detection
+ if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ this.set(NAME, 'Brave');
+ }
+ this.set(MAJOR, majorize(this.get(VERSION)));
+ }
+ UAParserBrowser.prototype = new UAParserItem();
+ UAParserBrowser.prototype.parseCH = function () {
+ var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS];
+ if (brands) {
+ for (var i in brands) {
+ var brandName = brands[i].brand,
+ brandVersion = brands[i].version;
+ if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
+ this.set(NAME, strip(GOOGLE+' ', brandName))
+ .set(VERSION, brandVersion)
+ .set(MAJOR, majorize(brandVersion));
+ }
+ }
+ }
+ return this;
+ };
+
+ function UAParserCPU (ua, cpuMap, uach) {
+ UAParserItem.call(this, [
+ ua,
+ uach,
+ UA_CPU,
+ cpuMap,
+ [ARCHITECTURE],
+ [],
+ null,
+ [ARCHITECTURE]
+ ]);
+ this.parse();
+ }
+ UAParserCPU.prototype = new UAParserItem();
+ UAParserCPU.prototype.parseCH = function () {
+ var archName = this.uaCH[ARCHITECTURE];
+ if (archName) {
+ archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY;
+ rgxMapper.call(this.data, archName, this.rgxMap);
+ }
+ return this;
+ };
+
+ function UAParserDevice (ua, deviceMap, uach) {
+ UAParserItem.call(this, [
+ ua,
+ uach,
+ UA_DEVICE,
+ deviceMap,
+ [TYPE, MODEL, VENDOR],
+ [],
+ null,
+ [VENDOR, MODEL]
+ ]);
+ this.parse();
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ this.set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
+ }
+ }
+ UAParserDevice.prototype = new UAParserItem();
+ UAParserDevice.prototype.parseCH = function () {
+ if (this.uaCH[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ if (this.uaCH[MODEL]) {
+ this.set(MODEL, this.uaCH[MODEL]);
+ }
+ return this;
+ };
+
+ function UAParserEngine (ua, engineMap) {
+ UAParserItem.call(this, [
+ ua,
+ null,
+ UA_ENGINE,
+ engineMap,
+ [NAME, VERSION],
+ [VERSION],
+ null,
+ [NAME, VERSION]
+ ]);
+ this.parse();
+ }
+ UAParserEngine.prototype = new UAParserItem();
+
+ function UAParserOS (ua, osMap, uach) {
+ UAParserItem.call(this, [
+ ua,
+ uach,
+ UA_OS,
+ osMap,
+ [NAME, VERSION],
+ [VERSION],
+ ' ?os$',
+ [NAME, VERSION]
+ ]);
+ this.parse();
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
+ }
+ }
+ UAParserOS.prototype = new UAParserItem();
+ UAParserOS.prototype.parseCH = function () {
+ var osName = this.uaCH[PLATFORM];
+ if(osName) {
+ var osVersion = this.uaCH[PLATFORMVER];
+ osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
+ }
+ return this;
+ };
+
+ function UAParserResult (ua, resMap, uach) {
+ UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]);
+ }
+ UAParserResult.prototype = new UAParserItem();
+
+ function UAParser (ua, extensions, headers) {
+
+ if (typeof ua === OBJ_TYPE) {
+ if (isExtensions(ua)) {
+ if (typeof extensions === OBJ_TYPE) {
+ headers = extensions; // case UAParser(extensions, headers)
+ }
+ extensions = ua; // case UAParser(extensions)
+ } else {
+ headers = ua; // case UAParser(headers)
+ extensions = undefined;
+ }
+ ua = undefined;
+ } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) {
+ headers = extensions; // case UAParser(ua, headers)
+ extensions = undefined;
+ }
+
+ if (!(this instanceof UAParser)) {
+ return new UAParser(ua, extensions, headers).getResult();
+ }
+
+ var userAgent = ua ||
+ ((NAVIGATOR && NAVIGATOR.userAgent) ?
+ NAVIGATOR.userAgent :
+ (headers && headers[USER_AGENT] ?
+ headers[USER_AGENT] :
+ EMPTY)),
+
+ HTTP_UACH = new UAParserDataCH(headers, true),
+
+ regexMap = extensions ?
+ extend(regexes, extensions) :
+ regexes;
+
+ // public methods
+ this.getBrowser = function () {
+ return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get();
+ };
+
+ this.getCPU = function () {
+ return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get();
+ };
+
+ this.getDevice = function () {
+ return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get();
+ };
+
+ this.getEngine = function () {
+ return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get();
+ };
+
+ this.getOS = function () {
+ return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get();
+ };
+
+ this.getResult = function () {
+ return new UAParserResult(userAgent, regexMap, HTTP_UACH)
+ .set('ua', userAgent)
+ .set('ua_ch', HTTP_UACH)
+ .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();
+ };
+
+ this.getUA = function () {
+ return userAgent;
+ };
+
+ this.setUA = function (ua) {
+ userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
+ return this;
+ };
+
+ 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]);
+
+ export {UAParser};
\ No newline at end of file
diff --git a/package.json b/package.json
index 37854dce2..df2cc2d1b 100644
--- a/package.json
+++ b/package.json
@@ -139,14 +139,20 @@
"yuanyang ",
"Yun Young-jin ",
"Zach Bjornson "
- ],
+ ],
+ "type": "commonjs",
"main": "src/ua-parser.js",
+ "module": "dist/ua-parser.mjs",
+ "exports": {
+ "require": "./src/ua-parser.js",
+ "import": "./dist/ua-parser.mjs"
+ },
"files": [
"dist",
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle",
+ "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('dist/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'const window = undefined;').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"",
"test": "jshint src/ua-parser.js && mocha -R nyan test/test.js",
"test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js",
"verup": "node ./node_modules/verup",
diff --git a/readme.md b/readme.md
index 93f5c8159..2733a3005 100644
--- a/readme.md
+++ b/readme.md
@@ -473,6 +473,12 @@ http.createServer(function (req, res) {
console.log('Server running at http://127.0.0.1:1337/');
```
+## Using ES Modules
+
+```js
+import { UAParser } from 'ua-parser-js';
+```
+
## Using TypeScript
```sh
From df1a1c7bb4f6f8742db4a8185622411e6cd13ad4 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 18 Mar 2023 18:50:42 +0700
Subject: [PATCH 044/388] Fix #624 - Detect Amazon Echo
---
src/ua-parser.js | 4 +++-
test/device-test.json | 27 +++++++++++++++++++++++++++
2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 3f703533c..d5daf6e05 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -542,7 +542,7 @@
// Amazon
/(alexa)webm/i,
- /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk
+ /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i, // Kindle Fire without Silk / Echo Show
/(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD
], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [
/((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone
@@ -727,6 +727,8 @@
/(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla
], [VENDOR, [TYPE, EMBEDDED]], [
+ /(aeobc)\b/i // Echo Dot
+ ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [
////////////////////
// MIXED (GENERIC)
diff --git a/test/device-test.json b/test/device-test.json
index c40361f13..d23aba1e0 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -1638,6 +1638,33 @@
"type": "tablet"
}
},
+ {
+ "desc": "Echo Show 5",
+ "ua": "Mozilla/5.0 (Linux; Android 5.1; AEORK Build/LVY48F; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Amazon",
+ "model": "AEORK",
+ "type": "tablet"
+ }
+ },
+ {
+ "desc": "Echo Show 8",
+ "ua": "Mozilla/5.0 (Linux; Android 7.1; AEOCH) AppleWebKit/537.36 (KHTML, like Gecko) Silk/77.2.21 like Chrome/77.0.3865.92 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Amazon",
+ "model": "AEOCH",
+ "type": "tablet"
+ }
+ },
+ {
+ "desc": "Echo Dot",
+ "ua": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEOBC Build/LVY48F)",
+ "expect": {
+ "vendor": "Amazon",
+ "model": "AEOBC",
+ "type": "embedded"
+ }
+ },
{
"desc": "Samsung Galaxy A21s",
"ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-A217F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.0 Chrome/75.0.3770.143 Mobile Safari/537.36",
From 6b3fc3e0f10e191b24f6f66af0cfe18a1a5ea84f Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 20 Mar 2023 01:17:04 +0700
Subject: [PATCH 045/388] Small refactor
---
package.json | 2 +-
src/ua-parser.js | 544 ++++++++++++++++++++--------------------------
test/es6-test.mjs | 17 ++
test/test.js | 2 +
4 files changed, 259 insertions(+), 306 deletions(-)
create mode 100644 test/es6-test.mjs
diff --git a/package.json b/package.json
index df2cc2d1b..db710b2be 100644
--- a/package.json
+++ b/package.json
@@ -153,7 +153,7 @@
],
"scripts": {
"build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('dist/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'const window = undefined;').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"",
- "test": "jshint src/ua-parser.js && mocha -R nyan test/test.js",
+ "test": "jshint src/ua-parser.js && mocha -R spec test/test.js && mocha -R spec test/es6-test.mjs",
"test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js",
"verup": "node ./node_modules/verup",
"version": "node ./node_modules/verup 0"
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 558dca77e..363482d1f 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -95,7 +95,18 @@
// Helper
//////////
- var extend = function (regexes, extensions) {
+ var assignFromEntries = 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;
+ },
+ 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];
@@ -110,18 +121,13 @@
return enums;
},
has = function (str1, str2) {
- return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
- },
- initialize = 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;
+ if (typeof str1 === OBJ_TYPE && str1.length > 0) {
+ for (var i in str1) {
+ if (lowerize(str1[i]) == lowerize(str2)) return true;
}
+ return false;
}
- return this;
+ return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
},
isExtensions = function (obj) {
for (var prop in obj) {
@@ -138,8 +144,8 @@
}
return arr;
},
- lowerize = function (str, rgx) {
- return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str;
+ lowerize = function (str) {
+ return typeof(str) === STR_TYPE ? str.toLowerCase() : str;
},
majorize = function (version) {
return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
@@ -270,7 +276,7 @@
// Regex map
/////////////
- var regexes = {
+ var defaultRegexes = {
browser : [[
@@ -845,15 +851,108 @@
]
};
+ /////////////////
+ // Factories
+ ////////////////
+
+ var defaultProps = (function () {
+ var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
+ assignFromEntries.call(props.init, [
+ [UA_BROWSER, [NAME, VERSION, MAJOR]],
+ [UA_CPU, [ARCHITECTURE]],
+ [UA_DEVICE, [TYPE, MODEL, VENDOR]],
+ [UA_ENGINE, [NAME, VERSION]],
+ [UA_OS, [NAME, VERSION]]
+ ]);
+ assignFromEntries.call(props.isIgnore, [
+ [UA_BROWSER, [VERSION, MAJOR]],
+ [UA_ENGINE, [VERSION]],
+ [UA_OS, [VERSION]]
+ ]);
+ assignFromEntries.call(props.isIgnoreRgx, [
+ [UA_BROWSER, / ?browser$/i],
+ [UA_OS, / ?os$/i]
+ ]);
+ assignFromEntries.call(props.toString, [
+ [UA_BROWSER, [NAME, VERSION]],
+ [UA_CPU, [ARCHITECTURE]],
+ [UA_DEVICE, [VENDOR, MODEL]],
+ [UA_ENGINE, [NAME, VERSION]],
+ [UA_OS, [NAME, VERSION]]
+ ]);
+ return props;
+ })();
+
+ var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
+
+ var init_props = defaultProps.init[itemType],
+ is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
+ is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0,
+ toString_props = defaultProps.toString[itemType] || 0;
+
+ function UAParserData () {
+ assignFromEntries.call(this, init_props);
+ }
+ UAParserData.prototype.withClientHints = function () {
+
+ // nodejs / non-client-hints browsers
+ if (!NAVIGATOR_UADATA) {
+ return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
+ }
+
+ // browsers based on chromium 85+
+ return NAVIGATOR_UADATA
+ .getHighEntropyValues(CH_ALL_VALUES)
+ .then(function (res) {
+ var JS_UACH = new UAParserDataCH(res, false);
+ return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get();
+ });
+ };
+
+ if (itemType != UA_RESULT) {
+ UAParserData.prototype.is = function (strToCheck) {
+ var is = false;
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
+ is = true;
+ if (strToCheck != UNDEF_TYPE) break;
+ } else if (strToCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
+ }
+ }
+ return is;
+ };
+ UAParserData.prototype.toString = function () {
+ var str = EMPTY;
+ for (var i in toString_props) {
+ if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : EMPTY) + this[toString_props[i]];
+ }
+ }
+ return str || UNDEF_TYPE;
+ };
+ }
+
+ if (!NAVIGATOR_UADATA) {
+ UAParserData.prototype.then = function (cb) {
+ cb(this);
+ return this;
+ };
+ }
+
+ return new UAParserData();
+ };
+
/////////////////
// Constructor
////////////////
function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
- initialize.call(this, CH_ALL_VALUES);
+ assignFromEntries.call(this, CH_ALL_VALUES);
if (isHTTP_UACH) {
- initialize.call(this, [
+ assignFromEntries.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
[BRANDS, itemListToArray(uach[CH_HEADER])],
@@ -872,268 +971,126 @@
return this;
}
- function UAParserItem (data) {
- if (!data) return;
- this.ua = data[0];
- this.uaCH = data[1];
- this.rgxMap = data[3];
- this.data = (function (data) {
- var ua = data[0],
- uaCH = data[1],
- itemType = data[2],
- rgxMap = data[3],
- init_props = data[4],
- is_ignoreProps = data[5],
- is_ignoreRgx = data[6],
- toString_props = data[7];
-
- function UAParserData () {
- initialize.call(this, init_props);
- }
- UAParserData.prototype.withClientHints = function () {
-
- // nodejs / non-client-hints browsers
- if (!NAVIGATOR_UADATA) {
-
- var HTTP_UACH = uaCH;
- switch (itemType) {
- case UA_BROWSER:
- return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get();
- case UA_CPU:
- return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get();
- case UA_DEVICE:
- return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get();
- case UA_ENGINE:
- return new UAParserEngine(ua, rgxMap).get();
- case UA_OS:
- return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get();
- default :
- return new UAParserResult(ua, rgxMap, HTTP_UACH)
- .set('ua', ua)
- .set('ua_ch', uaCH)
- .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get())
- .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get())
- .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get())
- .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
- .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get())
- .get();
- }
+ function UAParserItem (itemType, ua, rgxMap, uaCH) {
+ assignFromEntries.call(this, [
+ ['itemType', itemType],
+ ['ua', ua],
+ ['uaCH', uaCH],
+ ['rgxMap', rgxMap],
+ ['data', createUAParserData(itemType, ua, rgxMap, uaCH)]
+ ]);
+ this.parse();
+ switch(this.itemType) {
+ case UA_BROWSER:
+ // Brave-specific detection
+ if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ this.set(NAME, 'Brave');
}
-
- // browsers based on chromium 85+
- return NAVIGATOR_UADATA
- .getHighEntropyValues(CH_ALL_VALUES)
- .then(function (res) {
-
- var JS_UACH = new UAParserDataCH(res, false);
- switch (itemType) {
- case UA_BROWSER:
- return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get();
- case UA_CPU:
- return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get();
- case UA_DEVICE:
- return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get();
- case UA_ENGINE:
- return new UAParserEngine(ua, rgxMap).get();
- case UA_OS:
- return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get();
- default :
- return new UAParserResult(ua, rgxMap, JS_UACH)
- .set('ua', ua)
- .set('ua_ch', JS_UACH)
- .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get())
- .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get())
- .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get())
- .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
- .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get())
- .get();
- }
- });
- };
- if(itemType != UA_RESULT) {
- UAParserData.prototype.is = function (strToCheck) {
- var is = false;
- for (var i in this) {
- if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
- is = true;
- if (strToCheck != UNDEF_TYPE) break;
- } else if (strToCheck == UNDEF_TYPE && is) {
- is = !is;
- break;
- }
- }
- return is;
- };
- UAParserData.prototype.toString = function () {
- var str = EMPTY;
- for (var i in toString_props) {
- if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
- str += (str ? ' ' : EMPTY) + this[toString_props[i]];
- }
- }
- return str ? str : UNDEF_TYPE;
- };
- }
- if (!NAVIGATOR_UADATA) {
- UAParserData.prototype.then = function (cb) {
- cb(this);
- return this;
+ this.set(MAJOR, majorize(this.get(VERSION)));
+ break;
+ case UA_DEVICE:
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ this.set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
+ }
+ break;
+ case UA_OS:
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
+ }
+ break;
+ case UA_RESULT:
+ var createUAParserItem = function (itemType) {
+ return new UAParserItem(itemType, ua, rgxMap, uaCH).get();
};
- }
- return new UAParserData();
- })(data);
+ this.set('ua', ua)
+ .set('ua_ch', uaCH)
+ .set(UA_BROWSER, createUAParserItem(UA_BROWSER))
+ .set(UA_CPU, createUAParserItem(UA_CPU))
+ .set(UA_DEVICE, createUAParserItem(UA_DEVICE))
+ .set(UA_ENGINE, createUAParserItem(UA_ENGINE))
+ .set(UA_OS, createUAParserItem(UA_OS))
+ .get();
+ }
+ return this;
}
UAParserItem.prototype.get = function (prop) {
if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
UAParserItem.prototype.parse = function () {
- rgxMapper.call(this.data, this.ua, this.rgxMap);
- return this;
- };
- UAParserItem.prototype.set = function (prop, val) {
- this.data[prop] = val;
- return this;
- };
-
- function UAParserBrowser (ua, browserMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_BROWSER,
- browserMap,
- [NAME, VERSION, MAJOR],
- [VERSION, MAJOR],
- ' ?browser$',
- [NAME, VERSION]
- ]);
- this.parse();
- // Brave-specific detection
- if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
- this.set(NAME, 'Brave');
- }
- this.set(MAJOR, majorize(this.get(VERSION)));
- }
- UAParserBrowser.prototype = new UAParserItem();
- UAParserBrowser.prototype.parseCH = function () {
- var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS];
- if (brands) {
- for (var i in brands) {
- var brandName = brands[i].brand,
- brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
- this.set(NAME, strip(GOOGLE+' ', brandName))
- .set(VERSION, brandVersion)
- .set(MAJOR, majorize(brandVersion));
- }
- }
+ if (this.itemType != UA_RESULT) {
+ rgxMapper.call(this.data, this.ua, this.rgxMap[this.itemType]);
}
return this;
};
-
- function UAParserCPU (ua, cpuMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_CPU,
- cpuMap,
- [ARCHITECTURE],
- [],
- null,
- [ARCHITECTURE]
- ]);
- this.parse();
- }
- UAParserCPU.prototype = new UAParserItem();
- UAParserCPU.prototype.parseCH = function () {
- var archName = this.uaCH[ARCHITECTURE];
- if (archName) {
- archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY;
- rgxMapper.call(this.data, archName, this.rgxMap);
- }
- return this;
- };
-
- function UAParserDevice (ua, deviceMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_DEVICE,
- deviceMap,
- [TYPE, MODEL, VENDOR],
- [],
- null,
- [VENDOR, MODEL]
- ]);
- this.parse();
- if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
- this.set(MODEL, 'iPad')
- .set(TYPE, TABLET);
- }
- }
- UAParserDevice.prototype = new UAParserItem();
- UAParserDevice.prototype.parseCH = function () {
- if (this.uaCH[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- if (this.uaCH[MODEL]) {
- this.set(MODEL, this.uaCH[MODEL]);
+ UAParserItem.prototype.parseCH = function () {
+ var ua = this.ua,
+ uaCH = this.uaCH,
+ rgxMap = this.rgxMap;
+
+ switch (this.itemType) {
+ case UA_BROWSER:
+ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
+ if (brands) {
+ for (var i in brands) {
+ var brandName = brands[i].brand,
+ brandVersion = brands[i].version;
+ if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
+ this.set(NAME, strip(GOOGLE+' ', brandName))
+ .set(VERSION, brandVersion)
+ .set(MAJOR, majorize(brandVersion));
+ }
+ }
+ }
+ break;
+ case UA_CPU:
+ var archName = uaCH[ARCHITECTURE];
+ if (archName) {
+ archName += (archName && uaCH[BITNESS] == '64') ? '64' : EMPTY;
+ rgxMapper.call(this.data, archName, rgxMap[this.itemType]);
+ }
+ break;
+ case UA_DEVICE:
+ if (uaCH[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ if (uaCH[MODEL]) {
+ this.set(MODEL, uaCH[MODEL]);
+ }
+ break;
+ case UA_OS:
+ var osName = uaCH[PLATFORM];
+ if(osName) {
+ var osVersion = uaCH[PLATFORMVER];
+ osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
+ }
+ break;
+ case UA_RESULT:
+ var createUAParserItemWithCH = function (itemType) {
+ return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
+ };
+ this.set('ua', ua)
+ .set('ua_ch', uaCH)
+ .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
+ .set(UA_CPU, createUAParserItemWithCH(UA_CPU))
+ .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
+ .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
+ .set(UA_OS, createUAParserItemWithCH(UA_OS));
}
return this;
};
-
- function UAParserEngine (ua, engineMap) {
- UAParserItem.call(this, [
- ua,
- null,
- UA_ENGINE,
- engineMap,
- [NAME, VERSION],
- [VERSION],
- null,
- [NAME, VERSION]
- ]);
- this.parse();
- }
- UAParserEngine.prototype = new UAParserItem();
-
- function UAParserOS (ua, osMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_OS,
- osMap,
- [NAME, VERSION],
- [VERSION],
- ' ?os$',
- [NAME, VERSION]
- ]);
- this.parse();
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
- }
- }
- UAParserOS.prototype = new UAParserItem();
- UAParserOS.prototype.parseCH = function () {
- var osName = this.uaCH[PLATFORM];
- if(osName) {
- var osVersion = this.uaCH[PLATFORMVER];
- osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
- this.set(NAME, osName)
- .set(VERSION, osVersion);
- }
+ UAParserItem.prototype.set = function (prop, val) {
+ this.data[prop] = val;
return this;
};
- function UAParserResult (ua, resMap, uach) {
- UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]);
- }
- UAParserResult.prototype = new UAParserItem();
-
function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) {
@@ -1166,52 +1123,29 @@
HTTP_UACH = new UAParserDataCH(headers, true),
regexMap = extensions ?
- extend(regexes, extensions) :
- regexes;
-
- // public methods
- this.getBrowser = function () {
- return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get();
- };
-
- this.getCPU = function () {
- return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get();
- };
+ extend(defaultRegexes, extensions) :
+ defaultRegexes,
- this.getDevice = function () {
- return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get();
- };
-
- this.getEngine = function () {
- return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get();
- };
-
- this.getOS = function () {
- return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get();
- };
-
- this.getResult = function () {
- return new UAParserResult(userAgent, regexMap, HTTP_UACH)
- .set('ua', userAgent)
- .set('ua_ch', HTTP_UACH)
- .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();
- };
-
- this.getUA = function () {
- return userAgent;
- };
-
- this.setUA = function (ua) {
- userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
- return this;
- };
+ createUAParserItemFunc = function (itemType) {
+ return function () {
+ return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH).get();
+ };
+ };
- this.setUA(userAgent);
+ // public methods
+ assignFromEntries.call(this, [
+ ['getBrowser', createUAParserItemFunc(UA_BROWSER)],
+ ['getCPU', createUAParserItemFunc(UA_CPU)],
+ ['getDevice', createUAParserItemFunc(UA_DEVICE)],
+ ['getEngine', createUAParserItemFunc(UA_ENGINE)],
+ ['getOS', createUAParserItemFunc(UA_OS)],
+ ['getResult', createUAParserItemFunc(UA_RESULT)],
+ ['getUA', function () { return userAgent; }],
+ ['setUA', function (ua) {
+ userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
+ return this;
+ }]
+ ]).setUA(userAgent);
return this;
}
diff --git a/test/es6-test.mjs b/test/es6-test.mjs
new file mode 100644
index 000000000..4d8495872
--- /dev/null
+++ b/test/es6-test.mjs
@@ -0,0 +1,17 @@
+import { UAParser } from '../dist/ua-parser.mjs'
+import * as assert from 'assert'
+
+describe('Returns', () => {
+ it('getResult() should returns object', () => {
+ 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 },
+ cpu: { architecture: undefined },
+ device: { vendor: undefined, model: undefined, type: undefined },
+ engine: { name: undefined, version: undefined},
+ os: { name: undefined, version: undefined }
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/test.js b/test/test.js
index 39e948e1d..c744aa51f 100644
--- a/test/test.js
+++ b/test/test.js
@@ -195,6 +195,7 @@ describe('is() utility method', function () {
assert.strictEqual(uap.getBrowser().name, "IEMobile");
assert.strictEqual(uap.getBrowser().is("IEMobile"), true);
assert.strictEqual(uap.getBrowser().is("IE"), false);
+ assert.strictEqual(uap.getBrowser().is("11.0"), false);
});
it('Should ignore "Browser" suffix', function () {
@@ -204,6 +205,7 @@ describe('is() utility method', function () {
it('Should ignore case', function () {
assert.strictEqual(uap.getEngine().name, "Trident");
assert.strictEqual(uap.getEngine().is("tRiDeNt"), true);
+ assert.strictEqual(uap.getEngine().is("7.0"), false);
});
it('Should get exact name', function () {
From a1d816ae21e18c8debbe5f23657a0ce750290317 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 20 Mar 2023 23:36:51 +0700
Subject: [PATCH 046/388] Fix #519 #521 #616 - Improve iPhone & iOS detection
---
src/ua-parser.js | 3 ++-
test/device-test.json | 11 +++++++++++
test/os-test.json | 9 +++++++++
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 363482d1f..02f10b244 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -462,7 +462,7 @@
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
// Apple
- /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone
+ /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone
], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
/\((ipad);[-\w\),; ]+apple/i, // iPad
/applecoremedia\/[\w\.]+ \((ipad)/i,
@@ -789,6 +789,7 @@
// iOS/macOS
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
+ /ios;fbsv\/([\d\.]+)/i,
/cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i,
diff --git a/test/device-test.json b/test/device-test.json
index d23aba1e0..a7cb4b73f 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -3136,6 +3136,17 @@
"desc": "FaceBook Mobile App",
"ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]",
"expect": {
+ "vendor": "Apple",
+ "model": "iPhone12,1",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Issue #519",
+ "ua": "ios/iPhone/14.2/SOME_CUSTOM_APP_VERSION",
+ "expect": {
+ "vendor": "Apple",
+ "model": "iPhone",
"type": "mobile"
}
},
diff --git a/test/os-test.json b/test/os-test.json
index f720a1aa6..b21274cbf 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -764,6 +764,15 @@
"version" : "undefined"
}
},
+ {
+ "desc": "iOS with FaceBook Mobile App",
+ "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]",
+ "expect":
+ {
+ "name" : "iOS",
+ "version" : "13.6.1"
+ }
+ },
{
"desc" : "watchOS",
"ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]",
From 49d6422ebd173d90d642ba4717de77e0439291c6 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 22 Mar 2023 10:53:15 +0700
Subject: [PATCH 047/388] Mistype in iPad detection
---
src/ua-parser.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 02f10b244..08ea12811 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -994,7 +994,7 @@
this.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad')
.set(TYPE, TABLET);
}
From 33df5dc698ee62034ae7fa1ffa4497d0280d0d56 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 22 Mar 2023 11:02:26 +0700
Subject: [PATCH 048/388] Fix #637 - Detect Safari on iPhone as Safari Mobile
---
src/ua-parser.js | 2 ++
test/browser-test.json | 10 ++++++++++
2 files changed, 12 insertions(+)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 08ea12811..ecf0fc345 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -382,6 +382,8 @@
/version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile
], [VERSION, [NAME, 'Safari '+SUFFIX_MOBILE]], [
+ /iphone .*mobile(?:\/\w+ | ?)safari/i
+ ], [[NAME, 'Safari '+SUFFIX_MOBILE]], [
/version\/([\w\.\,]+) .*(safari)/i // Safari
], [VERSION, NAME], [
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
diff --git a/test/browser-test.json b/test/browser-test.json
index 20e7d54f3..3942c50ae 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -758,6 +758,16 @@
"major" : "4"
}
},
+ {
+ "desc" : "Mobile Safari",
+ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Safari/604.1",
+ "expect" :
+ {
+ "name" : "Safari Mobile",
+ "version" : "undefined",
+ "major" : "undefined"
+ }
+ },
{
"desc" : "Mosaic",
"ua" : "NCSA_Mosaic/2.6 (X11; SunOS 4.1.3 sun4m)",
From 33f02099d1bf2e074446c0608c4fbfcd22371475 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 22 Mar 2023 11:18:52 +0700
Subject: [PATCH 049/388] Fix #678 - Improve Yandex detection
---
src/ua-parser.js | 2 +-
test/browser-test.json | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index ecf0fc345..6035a1ff6 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -319,7 +319,7 @@
], [VERSION, [NAME, 'Konqueror']], [
/trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11
], [VERSION, [NAME, 'IE']], [
- /yabrowser\/([\w\.]+)/i // Yandex
+ /ya(?:search)?browser\/([\w\.]+)/i // Yandex
], [VERSION, [NAME, 'Yandex']], [
/(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
], [[NAME, /(.+)/, '$1 Secure '+SUFFIX_BROWSER], VERSION], [
diff --git a/test/browser-test.json b/test/browser-test.json
index 3942c50ae..63a501a9b 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -1308,6 +1308,16 @@
"major" : "1"
}
},
+ {
+ "desc" : "Yandex",
+ "ua" : "Mozilla/5.0 (Linux; arm_64; Android 11; M2101K7AG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.125 YaApp_Android/22.70 YaSearchBrowser/22.70 BroPP/1.0 SA/3 Mobile Safari/537.36",
+ "expect" :
+ {
+ "name" : "Yandex",
+ "version" : "22.70",
+ "major" : "22"
+ }
+ },
{
"desc" : "Puffin",
"ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo P2a42 Build/MMB29M; en-us) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Puffin/6.0.8.15804AP",
From 172f57ffea929a663dc26b5facdf3bd035251004 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 22 Mar 2023 11:51:28 +0700
Subject: [PATCH 050/388] Fix #636 - Add new browser: HeyTap | Fix #248 - Add
'Mobile' prefix for mobile version of Chrome, Firefox, Safari
---
readme.md | 6 +++---
src/ua-parser.js | 35 ++++++++++++++++++-----------------
test/browser-test.json | 30 ++++++++++++++++++++----------
3 files changed, 41 insertions(+), 30 deletions(-)
diff --git a/readme.md b/readme.md
index 90e72730d..7ad187291 100644
--- a/readme.md
+++ b/readme.md
@@ -102,16 +102,16 @@ The methods are self explanatory, here's a small overview on all the available m
# Possible 'browser.name':
2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG,
BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera,
-Chrome [Mobile/Headless/WebView], Chromium, Cobalt, Comodo Dragon, Dillo,
+[Mobile] Chrome [Headless/WebView], Chromium, Cobalt, Comodo Dragon, Dillo,
Dolphin, Doris, DuckDuckGo, Edge, Electron, Epiphany, Facebook, Falkon, Fennec,
-Firebird, Firefox [Mobile/Focus/Reality], Flock, Flow, GSA, GoBrowser,
+Firebird, [Mobile] Firefox [Focus/Reality], Flock, Flow, GSA, GoBrowser, HeyTap,
Huawei Browser, ICE Browser, IE, IEMobile, IceApe, IceCat, IceDragon, Iceweasel,
Instagram, Iridium, Iron, Jasmine, Kakao[Story/Talk], K-Meleon, Kindle, Klar,
Konqueror, LBBROWSER, Line, LinkedIn, Links, Lunascape, Lynx, MIUI Browser,
Maemo Browser, Maemo, Maxthon, MetaSr Midori, Minimo, Mosaic, Mozilla, NetFront,
NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb,
Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris,
-Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, Safari [Mobile],
+Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, [Mobile] Safari,
Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim,
SlimBrowser, Swiftfox, Tesla, Tizen Browser, UCBrowser, UP.Browser, Viera,
Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ...
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 6035a1ff6..931ef329d 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -75,8 +75,8 @@
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
ZTE = 'ZTE',
- SUFFIX_BROWSER = 'Browser',
- SUFFIX_MOBILE = 'Mobile',
+ PREFIX_MOBILE = 'Mobile ',
+ SUFFIX_BROWSER = ' Browser',
CHROME = 'Chrome',
EDGE = 'Edge',
FIREFOX = 'Firefox',
@@ -280,8 +280,9 @@
browser : [[
+ // Most common regardless engine
/\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
- ], [VERSION, [NAME, 'Chrome']], [
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [
/edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge
], [VERSION, [NAME, 'Edge']], [
@@ -305,11 +306,12 @@
// 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, aka ShouQ
+ // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo
+ /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi
/(weibo)__([\d\.]+)/i // Weibo
], [NAME, VERSION], [
/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
- ], [VERSION, [NAME, 'UC'+SUFFIX_BROWSER]], [
+ ], [VERSION, [NAME, 'UCBrowser']], [
/microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
/\bqbcore\/([\w\.]+).+microm/i
], [VERSION, [NAME, 'WeChat(Win) Desktop']], [
@@ -322,7 +324,7 @@
/ya(?:search)?browser\/([\w\.]+)/i // Yandex
], [VERSION, [NAME, 'Yandex']], [
/(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
- ], [[NAME, /(.+)/, '$1 Secure '+SUFFIX_BROWSER], VERSION], [
+ ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [
/\bfocus\/([\w\.]+)/i // Firefox Focus
], [VERSION, [NAME, FIREFOX+' Focus']], [
/\bopt\/([\w\.]+)/i // Opera Touch
@@ -334,13 +336,13 @@
/coast\/([\w\.]+)/i // Opera Coast
], [VERSION, [NAME, OPERA+' Coast']], [
/miuibrowser\/([\w\.]+)/i // MIUI Browser
- ], [VERSION, [NAME, 'MIUI '+SUFFIX_BROWSER]], [
+ ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [
/fxios\/([\w\.-]+)/i // Firefox for iOS
- ], [VERSION, [NAME, 'Firefox '+SUFFIX_MOBILE]], [
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [
/\bqihu|(qi?ho?o?|360)browser/i // 360
- ], [[NAME, '360 '+SUFFIX_BROWSER]], [
+ ], [[NAME, '360' + SUFFIX_BROWSER]], [
/(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i
- ], [[NAME, /(.+)/, '$1 '+SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser
+ ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser
/(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
], [[NAME, /_/g, ' '], VERSION], [
/(electron)\/([\w\.]+) safari/i, // Electron-based App
@@ -371,19 +373,18 @@
], [[NAME, CHROME+' WebView'], VERSION], [
/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
- ], [VERSION, [NAME, 'Android '+SUFFIX_BROWSER]], [
+ ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [
- /chrome\/([\w\.]+) mobile/i, // Chrome Mobile
- /(?:(?:android.+)crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
- ], [VERSION, [NAME, 'Chrome '+SUFFIX_MOBILE]], [
+ /chrome\/([\w\.]+) mobile/i // Chrome Mobile
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [
/(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia
], [NAME, VERSION], [
/version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile
- ], [VERSION, [NAME, 'Safari '+SUFFIX_MOBILE]], [
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Safari']], [
/iphone .*mobile(?:\/\w+ | ?)safari/i
- ], [[NAME, 'Safari '+SUFFIX_MOBILE]], [
+ ], [[NAME, PREFIX_MOBILE + 'Safari']], [
/version\/([\w\.\,]+) .*(safari)/i // Safari
], [VERSION, NAME], [
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
@@ -394,7 +395,7 @@
// Gecko based
/(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile
- ], [[NAME, 'Firefox '+SUFFIX_MOBILE], VERSION], [
+ ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [
/(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
], [[NAME, 'Netscape'], VERSION], [
/mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
diff --git a/test/browser-test.json b/test/browser-test.json
index 63a501a9b..0b0345f22 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -234,7 +234,7 @@
"ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3",
"expect" :
{
- "name" : "Chrome",
+ "name" : "Mobile Chrome",
"version" : "19.0.1084.60",
"major" : "19"
}
@@ -254,7 +254,7 @@
"ua" : "Mozilla/5.0 (Linux; U; Android-4.0.3; en-us; Galaxy Nexus Build/IML74K) AppleWebKit/535.7 (KHTML, like Gecko) CrMo/16.0.912.75 Mobile Safari/535.7",
"expect" :
{
- "name" : "Chrome",
+ "name" : "Mobile Chrome",
"version" : "16.0.912.75",
"major" : "16"
}
@@ -508,6 +508,16 @@
"major" : "1"
}
},
+ {
+ "desc" : "HeyTap",
+ "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.61 Safari/537.36 HeyTapBrowser/40.8.10.1",
+ "expect" :
+ {
+ "name" : "HeyTap",
+ "version" : "40.8.10.1",
+ "major" : "40"
+ }
+ },
{
"desc" : "HuaweiBrowser",
"ua" : "Mozilla/5.0 (Linux; Android 6.0.1; LYA-AL00;HMSCore/4.0.0 GMS/10.4 ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.64 HuaweiBrowser/10.0.3.102 Mobile Safari/537.36",
@@ -753,7 +763,7 @@
"ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7",
"expect" :
{
- "name" : "Safari Mobile",
+ "name" : "Mobile Safari",
"version" : "4.0.5",
"major" : "4"
}
@@ -763,7 +773,7 @@
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Safari/604.1",
"expect" :
{
- "name" : "Safari Mobile",
+ "name" : "Mobile Safari",
"version" : "undefined",
"major" : "undefined"
}
@@ -1413,7 +1423,7 @@
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) FxiOS/1.1 Mobile/13B143 Safari/601.1.46",
"expect" :
{
- "name" : "Firefox Mobile",
+ "name" : "Mobile Firefox",
"version" : "1.1",
"major" : "1"
}
@@ -1423,7 +1433,7 @@
"ua" : "Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4",
"expect" :
{
- "name" : "Firefox Mobile",
+ "name" : "Mobile Firefox",
"version" : "1.0",
"major" : "1"
}
@@ -1617,7 +1627,7 @@
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 15_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6,2 Mobile/15E148 Safari/604.1",
"expect" :
{
- "name" : "Safari Mobile",
+ "name" : "Mobile Safari",
"version" : "15.6,2",
"major" : "15"
}
@@ -1708,7 +1718,7 @@
"ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
"expect" :
{
- "name" : "Chrome Mobile",
+ "name" : "Mobile Chrome",
"version" : "58.0.3029.83",
"major" : "58"
}
@@ -1718,7 +1728,7 @@
"ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) FxiOS/7.5b3349 Mobile/14F89 Safari/603.2.4",
"expect" :
{
- "name" : "Firefox Mobile",
+ "name" : "Mobile Firefox",
"version" : "7.5b3349",
"major" : "7"
}
@@ -1728,7 +1738,7 @@
"ua" : "Mozilla/5.0 (Android 5.0; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0",
"expect" :
{
- "name" : "Firefox Mobile",
+ "name" : "Mobile Firefox",
"version" : "41.0",
"major" : "41"
}
From 3af8e1e272a252d3b55ab8815dfafc8e3d8a9589 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 23 Mar 2023 21:43:19 +0700
Subject: [PATCH 051/388] Upon creating new item, only pass regex map thats
relevant with its itemType
---
src/ua-parser.js | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 931ef329d..04a43b613 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -969,7 +969,7 @@
]);
} else {
for (var prop in uach) {
- if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]);
+ if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
}
}
return this;
@@ -1009,7 +1009,7 @@
break;
case UA_RESULT:
var createUAParserItem = function (itemType) {
- return new UAParserItem(itemType, ua, rgxMap, uaCH).get();
+ return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
};
this.set('ua', ua)
.set('ua_ch', uaCH)
@@ -1028,7 +1028,7 @@
};
UAParserItem.prototype.parse = function () {
if (this.itemType != UA_RESULT) {
- rgxMapper.call(this.data, this.ua, this.rgxMap[this.itemType]);
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
}
return this;
};
@@ -1055,8 +1055,8 @@
case UA_CPU:
var archName = uaCH[ARCHITECTURE];
if (archName) {
- archName += (archName && uaCH[BITNESS] == '64') ? '64' : EMPTY;
- rgxMapper.call(this.data, archName, rgxMap[this.itemType]);
+ if (archName && uaCH[BITNESS] == '64') archName += '64';
+ rgxMapper.call(this.data, archName, rgxMap);
}
break;
case UA_DEVICE:
@@ -1071,14 +1071,14 @@
var osName = uaCH[PLATFORM];
if(osName) {
var osVersion = uaCH[PLATFORMVER];
- osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
+ if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
this.set(NAME, osName)
.set(VERSION, osVersion);
}
break;
case UA_RESULT:
var createUAParserItemWithCH = function (itemType) {
- return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
+ return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
};
this.set('ua', ua)
.set('ua_ch', uaCH)
@@ -1132,7 +1132,7 @@
createUAParserItemFunc = function (itemType) {
return function () {
- return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH).get();
+ return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get();
};
};
From e70d09a1f8f72ef41658a3e9f0a079f0c7ae4229 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 25 Mar 2023 07:21:17 +0700
Subject: [PATCH 052/388] Fix #489: ARM arch detection & create test that
simulates HTTP headers sent from an Apple silicon
---
src/ua-parser.js | 2 +-
test/test.js | 30 ++++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 04a43b613..3fb1e122f 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -1056,7 +1056,7 @@
var archName = uaCH[ARCHITECTURE];
if (archName) {
if (archName && uaCH[BITNESS] == '64') archName += '64';
- rgxMapper.call(this.data, archName, rgxMap);
+ rgxMapper.call(this.data, archName + ';', rgxMap);
}
break;
case UA_DEVICE:
diff --git a/test/test.js b/test/test.js
index c744aa51f..8077e82da 100644
--- a/test/test.js
+++ b/test/test.js
@@ -483,6 +483,36 @@ describe('Map UA-CH headers', function () {
assert.strictEqual(uap.os.name, "Linux");
assert.strictEqual(uap.os.version, "x86_64");
});
+
+ it('Can detect Apple silicon from client hints data', function () {
+
+ // https://github.com/faisalman/ua-parser-js/issues/489#issuecomment-1479213579
+ const httpHeadersFromAppleSilicon = {
+ 'sec-ch-ua-arch' : 'arm',
+ 'sec-ch-ua-platform' : 'macOS',
+ 'sec-ch-ua-mobile' : '?0',
+ 'sec-ch-ua' : '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
+ 'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'
+ };
+
+ UAParser(httpHeadersFromAppleSilicon).withClientHints().then(function (ua) {
+
+ // Only works in Chrome
+ /*
+ if (ua.os.is("macOS") &&
+ ua.cpu.is("arm") &&
+ !ua.device.is("mobile") &&
+ !ua.device.is("tablet")) {
+ // possibly an Apple silicon device
+ }
+ */
+
+ assert.strictEqual(ua.os.is("macOS"), true);
+ assert.strictEqual(ua.cpu.is("arm"), true);
+ assert.strictEqual(ua.device.is("mobile"), false);
+ assert.strictEqual(ua.device.is("tablet"), false);
+ });
+ });
});
describe('Map UA-CH JS', () => {
From 82567c28ab15227dc864f35f113c6df8a1ec40b8 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 26 Mar 2023 11:57:17 +0700
Subject: [PATCH 053/388] Fix #639: Only check for Brave properties if given
userAgent match the current userAgent
---
src/ua-parser.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 3fb1e122f..bdf703603 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -987,7 +987,7 @@
switch(this.itemType) {
case UA_BROWSER:
// Brave-specific detection
- if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
this.set(NAME, 'Brave');
}
this.set(MAJOR, majorize(this.get(VERSION)));
From ba28d33d51c00bd39197821aec93c41acf1ef922 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 27 Mar 2023 22:25:17 +0700
Subject: [PATCH 054/388] Move generated .mjs file to /src
---
package.json | 10 +-
{dist => src}/ua-parser.mjs | 646 +++++++++++++++++-------------------
test/es6-test.mjs | 2 +-
3 files changed, 302 insertions(+), 356 deletions(-)
rename {dist => src}/ua-parser.mjs (77%)
diff --git a/package.json b/package.json
index db710b2be..b00b5c5c8 100644
--- a/package.json
+++ b/package.json
@@ -142,19 +142,19 @@
],
"type": "commonjs",
"main": "src/ua-parser.js",
- "module": "dist/ua-parser.mjs",
+ "module": "src/ua-parser.mjs",
"exports": {
"require": "./src/ua-parser.js",
- "import": "./dist/ua-parser.mjs"
+ "import": "./src/ua-parser.mjs"
},
"files": [
"dist",
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('dist/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'const window = undefined;').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"",
- "test": "jshint src/ua-parser.js && mocha -R spec test/test.js && mocha -R spec test/es6-test.mjs",
- "test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js",
+ "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"",
+ "test": "jshint src/ua-parser.js && mocha -R nyan test",
+ "test-ci": "jshint src/ua-parser.js && mocha -R spec test",
"verup": "node ./node_modules/verup",
"version": "node ./node_modules/verup 0"
},
diff --git a/dist/ua-parser.mjs b/src/ua-parser.mjs
similarity index 77%
rename from dist/ua-parser.mjs
rename to src/ua-parser.mjs
index 7d3aafd1e..1821d97dc 100644
--- a/dist/ua-parser.mjs
+++ b/src/ua-parser.mjs
@@ -1,6 +1,8 @@
// Generated ESM version of UAParser.js
// Source file: /src/ua-parser.js
+const window = undefined;
+
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v1.1.34
Copyright © 2012-2023 Faisal Salman
@@ -11,7 +13,7 @@
Source : https://github.com/faisalman/ua-parser-js */
/////////////////////////////////////////////////////////////////////////////////
-const window = undefined;
+
//////////////
// Constants
@@ -53,39 +55,37 @@ const window = undefined;
CH_HEADER_MODEL = CH_HEADER + '-model',
CH_HEADER_PLATFORM = CH_HEADER + '-platform',
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
- CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
+ CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
UA_BROWSER = 'browser',
UA_CPU = 'cpu',
UA_DEVICE = 'device',
UA_ENGINE = 'engine',
UA_OS = 'os',
- UA_RESULT = 'result';
-
- var AMAZON = 'Amazon',
- APPLE = 'Apple',
- ASUS = 'ASUS',
- BLACKBERRY = 'BlackBerry',
- GOOGLE = 'Google',
- HUAWEI = 'Huawei',
- LG = 'LG',
- MICROSOFT = 'Microsoft',
- MOTOROLA = 'Motorola',
- SAMSUNG = 'Samsung',
- SHARP = 'Sharp',
- SONY = 'Sony',
- SWISS = 'Swiss',
- XIAOMI = 'Xiaomi',
- ZEBRA = 'Zebra',
- ZTE = 'ZTE',
-
- BROWSER = 'Browser',
- CHROME = 'Chrome',
- EDGE = 'Edge',
- FIREFOX = 'Firefox',
- OPERA = 'Opera',
+ UA_RESULT = 'result',
+ AMAZON = 'Amazon',
+ APPLE = 'Apple',
+ ASUS = 'ASUS',
+ BLACKBERRY = 'BlackBerry',
+ GOOGLE = 'Google',
+ HUAWEI = 'Huawei',
+ LG = 'LG',
+ MICROSOFT = 'Microsoft',
+ MOTOROLA = 'Motorola',
+ SAMSUNG = 'Samsung',
+ SHARP = 'Sharp',
+ SONY = 'Sony',
+ SWISS = 'Swiss',
+ XIAOMI = 'Xiaomi',
+ ZEBRA = 'Zebra',
+ ZTE = 'ZTE',
+ PREFIX_MOBILE = 'Mobile ',
+ SUFFIX_BROWSER = ' Browser',
+ CHROME = 'Chrome',
+ EDGE = 'Edge',
+ FIREFOX = 'Firefox',
+ OPERA = 'Opera',
FACEBOOK = 'Facebook',
-
- WINDOWS = 'Windows';
+ WINDOWS = 'Windows';
var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ?
window.navigator :
@@ -98,7 +98,18 @@ const window = undefined;
// Helper
//////////
- var extend = function (regexes, extensions) {
+ var assignFromEntries = 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;
+ },
+ 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];
@@ -113,18 +124,13 @@ const window = undefined;
return enums;
},
has = function (str1, str2) {
- return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
- },
- initialize = 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;
+ if (typeof str1 === OBJ_TYPE && str1.length > 0) {
+ for (var i in str1) {
+ if (lowerize(str1[i]) == lowerize(str2)) return true;
}
+ return false;
}
- return this;
+ return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
},
isExtensions = function (obj) {
for (var prop in obj) {
@@ -141,8 +147,8 @@ const window = undefined;
}
return arr;
},
- lowerize = function (str, rgx) {
- return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str;
+ lowerize = function (str) {
+ return typeof(str) === STR_TYPE ? str.toLowerCase() : str;
},
majorize = function (version) {
return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
@@ -273,12 +279,13 @@ const window = undefined;
// Regex map
/////////////
- var regexes = {
+ var defaultRegexes = {
browser : [[
+ // Most common regardless engine
/\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
- ], [VERSION, [NAME, 'Chrome']], [
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [
/edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge
], [VERSION, [NAME, 'Edge']], [
@@ -302,11 +309,12 @@ const window = undefined;
// 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, aka ShouQ
+ // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo
+ /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi
/(weibo)__([\d\.]+)/i // Weibo
], [NAME, VERSION], [
/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
- ], [VERSION, [NAME, 'UC'+BROWSER]], [
+ ], [VERSION, [NAME, 'UCBrowser']], [
/microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
/\bqbcore\/([\w\.]+).+microm/i
], [VERSION, [NAME, 'WeChat(Win) Desktop']], [
@@ -316,10 +324,10 @@ const window = undefined;
], [VERSION, [NAME, 'Konqueror']], [
/trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11
], [VERSION, [NAME, 'IE']], [
- /yabrowser\/([\w\.]+)/i // Yandex
+ /ya(?:search)?browser\/([\w\.]+)/i // Yandex
], [VERSION, [NAME, 'Yandex']], [
/(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
- ], [[NAME, /(.+)/, '$1 Secure '+BROWSER], VERSION], [
+ ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [
/\bfocus\/([\w\.]+)/i // Firefox Focus
], [VERSION, [NAME, FIREFOX+' Focus']], [
/\bopt\/([\w\.]+)/i // Opera Touch
@@ -331,13 +339,13 @@ const window = undefined;
/coast\/([\w\.]+)/i // Opera Coast
], [VERSION, [NAME, OPERA+' Coast']], [
/miuibrowser\/([\w\.]+)/i // MIUI Browser
- ], [VERSION, [NAME, 'MIUI '+BROWSER]], [
- /fxios\/([-\w\.]+)/i // Firefox for iOS
- ], [VERSION, [NAME, FIREFOX]], [
+ ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [
+ /fxios\/([\w\.-]+)/i // Firefox for iOS
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [
/\bqihu|(qi?ho?o?|360)browser/i // 360
- ], [[NAME, '360 '+BROWSER]], [
+ ], [[NAME, '360' + SUFFIX_BROWSER]], [
/(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i
- ], [[NAME, /(.+)/, '$1 '+BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser
+ ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser
/(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
], [[NAME, /_/g, ' '], VERSION], [
/(electron)\/([\w\.]+) safari/i, // Electron-based App
@@ -368,14 +376,19 @@ const window = undefined;
], [[NAME, CHROME+' WebView'], VERSION], [
/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
- ], [VERSION, [NAME, 'Android '+BROWSER]], [
+ ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [
+
+ /chrome\/([\w\.]+) mobile/i // Chrome Mobile
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [
/(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia
], [NAME, VERSION], [
- /version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i // Mobile Safari
- ], [VERSION, [NAME, 'Mobile Safari']], [
- /version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile
+ /version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile
+ ], [VERSION, [NAME, PREFIX_MOBILE + 'Safari']], [
+ /iphone .*mobile(?:\/\w+ | ?)safari/i
+ ], [[NAME, PREFIX_MOBILE + 'Safari']], [
+ /version\/([\w\.\,]+) .*(safari)/i // Safari
], [VERSION, NAME], [
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
], [NAME, [VERSION, strMapper, oldSafariMap]], [
@@ -384,6 +397,8 @@ const window = undefined;
], [NAME, VERSION], [
// Gecko based
+ /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile
+ ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [
/(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
], [[NAME, 'Netscape'], VERSION], [
/mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
@@ -453,7 +468,7 @@ const window = undefined;
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
// Apple
- /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone
+ /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone
], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
/\((ipad);[-\w\),; ]+apple/i, // iPad
/applecoremedia\/[\w\.]+ \((ipad)/i,
@@ -543,7 +558,7 @@ const window = undefined;
// Amazon
/(alexa)webm/i,
- /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk
+ /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i, // Kindle Fire without Silk / Echo Show
/(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD
], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [
/((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone
@@ -728,6 +743,8 @@ const window = undefined;
/(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla
], [VENDOR, [TYPE, EMBEDDED]], [
+ /(aeobc)\b/i // Echo Dot
+ ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [
////////////////////
// MIXED (GENERIC)
@@ -778,6 +795,7 @@ const window = undefined;
// iOS/macOS
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
+ /ios;fbsv\/([\d\.]+)/i,
/cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i,
@@ -840,15 +858,108 @@ const window = undefined;
]
};
+ /////////////////
+ // Factories
+ ////////////////
+
+ var defaultProps = (function () {
+ var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
+ assignFromEntries.call(props.init, [
+ [UA_BROWSER, [NAME, VERSION, MAJOR]],
+ [UA_CPU, [ARCHITECTURE]],
+ [UA_DEVICE, [TYPE, MODEL, VENDOR]],
+ [UA_ENGINE, [NAME, VERSION]],
+ [UA_OS, [NAME, VERSION]]
+ ]);
+ assignFromEntries.call(props.isIgnore, [
+ [UA_BROWSER, [VERSION, MAJOR]],
+ [UA_ENGINE, [VERSION]],
+ [UA_OS, [VERSION]]
+ ]);
+ assignFromEntries.call(props.isIgnoreRgx, [
+ [UA_BROWSER, / ?browser$/i],
+ [UA_OS, / ?os$/i]
+ ]);
+ assignFromEntries.call(props.toString, [
+ [UA_BROWSER, [NAME, VERSION]],
+ [UA_CPU, [ARCHITECTURE]],
+ [UA_DEVICE, [VENDOR, MODEL]],
+ [UA_ENGINE, [NAME, VERSION]],
+ [UA_OS, [NAME, VERSION]]
+ ]);
+ return props;
+ })();
+
+ var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
+
+ var init_props = defaultProps.init[itemType],
+ is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
+ is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0,
+ toString_props = defaultProps.toString[itemType] || 0;
+
+ function UAParserData () {
+ assignFromEntries.call(this, init_props);
+ }
+ UAParserData.prototype.withClientHints = function () {
+
+ // nodejs / non-client-hints browsers
+ if (!NAVIGATOR_UADATA) {
+ return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
+ }
+
+ // browsers based on chromium 85+
+ return NAVIGATOR_UADATA
+ .getHighEntropyValues(CH_ALL_VALUES)
+ .then(function (res) {
+ var JS_UACH = new UAParserDataCH(res, false);
+ return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get();
+ });
+ };
+
+ if (itemType != UA_RESULT) {
+ UAParserData.prototype.is = function (strToCheck) {
+ var is = false;
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
+ is = true;
+ if (strToCheck != UNDEF_TYPE) break;
+ } else if (strToCheck == UNDEF_TYPE && is) {
+ is = !is;
+ break;
+ }
+ }
+ return is;
+ };
+ UAParserData.prototype.toString = function () {
+ var str = EMPTY;
+ for (var i in toString_props) {
+ if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
+ str += (str ? ' ' : EMPTY) + this[toString_props[i]];
+ }
+ }
+ return str || UNDEF_TYPE;
+ };
+ }
+
+ if (!NAVIGATOR_UADATA) {
+ UAParserData.prototype.then = function (cb) {
+ cb(this);
+ return this;
+ };
+ }
+
+ return new UAParserData();
+ };
+
/////////////////
// Constructor
////////////////
function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
- initialize.call(this, CH_ALL_VALUES);
+ assignFromEntries.call(this, CH_ALL_VALUES);
if (isHTTP_UACH) {
- initialize.call(this, [
+ assignFromEntries.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
[BRANDS, itemListToArray(uach[CH_HEADER])],
@@ -861,274 +972,132 @@ const window = undefined;
]);
} else {
for (var prop in uach) {
- if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]);
+ if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
}
}
return this;
}
- function UAParserItem (data) {
- if (!data) return;
- this.ua = data[0];
- this.uaCH = data[1];
- this.rgxMap = data[3];
- this.data = (function (data) {
- var ua = data[0],
- uaCH = data[1],
- itemType = data[2],
- rgxMap = data[3],
- init_props = data[4],
- is_ignoreProps = data[5],
- is_ignoreRgx = data[6],
- toString_props = data[7];
-
- function UAParserData () {
- initialize.call(this, init_props);
- }
- UAParserData.prototype.withClientHints = function () {
-
- // nodejs / non-client-hints browsers
- if (!NAVIGATOR_UADATA) {
-
- var HTTP_UACH = uaCH;
- switch (itemType) {
- case UA_BROWSER:
- return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get();
- case UA_CPU:
- return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get();
- case UA_DEVICE:
- return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get();
- case UA_ENGINE:
- return new UAParserEngine(ua, rgxMap).get();
- case UA_OS:
- return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get();
- default :
- return new UAParserResult(ua, rgxMap, HTTP_UACH)
- .set('ua', ua)
- .set('ua_ch', uaCH)
- .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get())
- .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get())
- .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get())
- .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
- .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get())
- .get();
- }
+ function UAParserItem (itemType, ua, rgxMap, uaCH) {
+ assignFromEntries.call(this, [
+ ['itemType', itemType],
+ ['ua', ua],
+ ['uaCH', uaCH],
+ ['rgxMap', rgxMap],
+ ['data', createUAParserData(itemType, ua, rgxMap, uaCH)]
+ ]);
+ this.parse();
+ switch(this.itemType) {
+ case UA_BROWSER:
+ // Brave-specific detection
+ if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ this.set(NAME, 'Brave');
}
-
- // browsers based on chromium 85+
- return NAVIGATOR_UADATA
- .getHighEntropyValues(CH_ALL_VALUES)
- .then(function (res) {
-
- var JS_UACH = new UAParserDataCH(res, false);
- switch (itemType) {
- case UA_BROWSER:
- return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get();
- case UA_CPU:
- return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get();
- case UA_DEVICE:
- return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get();
- case UA_ENGINE:
- return new UAParserEngine(ua, rgxMap).get();
- case UA_OS:
- return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get();
- default :
- return new UAParserResult(ua, rgxMap, JS_UACH)
- .set('ua', ua)
- .set('ua_ch', JS_UACH)
- .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get())
- .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get())
- .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get())
- .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get())
- .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get())
- .get();
- }
- });
- };
- if(itemType != UA_RESULT) {
- UAParserData.prototype.is = function (strToCheck) {
- var is = false;
- for (var i in this) {
- if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
- is = true;
- if (strToCheck != UNDEF_TYPE) break;
- } else if (strToCheck == UNDEF_TYPE && is) {
- is = !is;
- break;
- }
- }
- return is;
- };
- UAParserData.prototype.toString = function () {
- var str = EMPTY;
- for (var i in toString_props) {
- if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
- str += (str ? ' ' : EMPTY) + this[toString_props[i]];
- }
- }
- return str ? str : UNDEF_TYPE;
- };
- }
- if (!NAVIGATOR_UADATA) {
- UAParserData.prototype.then = function (cb) {
- cb(this);
- return this;
+ this.set(MAJOR, majorize(this.get(VERSION)));
+ break;
+ case UA_DEVICE:
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ this.set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
+ }
+ break;
+ case UA_OS:
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
+ }
+ break;
+ case UA_RESULT:
+ var createUAParserItem = function (itemType) {
+ return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
};
- }
- return new UAParserData();
- })(data);
+ this.set('ua', ua)
+ .set('ua_ch', uaCH)
+ .set(UA_BROWSER, createUAParserItem(UA_BROWSER))
+ .set(UA_CPU, createUAParserItem(UA_CPU))
+ .set(UA_DEVICE, createUAParserItem(UA_DEVICE))
+ .set(UA_ENGINE, createUAParserItem(UA_ENGINE))
+ .set(UA_OS, createUAParserItem(UA_OS))
+ .get();
+ }
+ return this;
}
UAParserItem.prototype.get = function (prop) {
if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
UAParserItem.prototype.parse = function () {
- rgxMapper.call(this.data, this.ua, this.rgxMap);
- return this;
- };
- UAParserItem.prototype.set = function (prop, val) {
- this.data[prop] = val;
- return this;
- };
-
- function UAParserBrowser (ua, browserMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_BROWSER,
- browserMap,
- [NAME, VERSION, MAJOR],
- [VERSION, MAJOR],
- ' ?browser$',
- [NAME, VERSION]
- ]);
- this.parse();
- // Brave-specific detection
- if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
- this.set(NAME, 'Brave');
- }
- this.set(MAJOR, majorize(this.get(VERSION)));
- }
- UAParserBrowser.prototype = new UAParserItem();
- UAParserBrowser.prototype.parseCH = function () {
- var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS];
- if (brands) {
- for (var i in brands) {
- var brandName = brands[i].brand,
- brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
- this.set(NAME, strip(GOOGLE+' ', brandName))
- .set(VERSION, brandVersion)
- .set(MAJOR, majorize(brandVersion));
- }
- }
+ if (this.itemType != UA_RESULT) {
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
}
return this;
};
-
- function UAParserCPU (ua, cpuMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_CPU,
- cpuMap,
- [ARCHITECTURE],
- [],
- null,
- [ARCHITECTURE]
- ]);
- this.parse();
- }
- UAParserCPU.prototype = new UAParserItem();
- UAParserCPU.prototype.parseCH = function () {
- var archName = this.uaCH[ARCHITECTURE];
- if (archName) {
- archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY;
- rgxMapper.call(this.data, archName, this.rgxMap);
- }
- return this;
- };
-
- function UAParserDevice (ua, deviceMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_DEVICE,
- deviceMap,
- [TYPE, MODEL, VENDOR],
- [],
- null,
- [VENDOR, MODEL]
- ]);
- this.parse();
- if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
- this.set(MODEL, 'iPad')
- .set(TYPE, TABLET);
- }
- }
- UAParserDevice.prototype = new UAParserItem();
- UAParserDevice.prototype.parseCH = function () {
- if (this.uaCH[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- if (this.uaCH[MODEL]) {
- this.set(MODEL, this.uaCH[MODEL]);
+ UAParserItem.prototype.parseCH = function () {
+ var ua = this.ua,
+ uaCH = this.uaCH,
+ rgxMap = this.rgxMap;
+
+ switch (this.itemType) {
+ case UA_BROWSER:
+ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
+ if (brands) {
+ for (var i in brands) {
+ var brandName = brands[i].brand,
+ brandVersion = brands[i].version;
+ if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
+ this.set(NAME, strip(GOOGLE+' ', brandName))
+ .set(VERSION, brandVersion)
+ .set(MAJOR, majorize(brandVersion));
+ }
+ }
+ }
+ 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]);
+ }
+ break;
+ case UA_OS:
+ var osName = uaCH[PLATFORM];
+ if(osName) {
+ var osVersion = uaCH[PLATFORMVER];
+ if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
+ }
+ break;
+ case UA_RESULT:
+ var createUAParserItemWithCH = function (itemType) {
+ return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
+ };
+ this.set('ua', ua)
+ .set('ua_ch', uaCH)
+ .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
+ .set(UA_CPU, createUAParserItemWithCH(UA_CPU))
+ .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
+ .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
+ .set(UA_OS, createUAParserItemWithCH(UA_OS));
}
return this;
};
-
- function UAParserEngine (ua, engineMap) {
- UAParserItem.call(this, [
- ua,
- null,
- UA_ENGINE,
- engineMap,
- [NAME, VERSION],
- [VERSION],
- null,
- [NAME, VERSION]
- ]);
- this.parse();
- }
- UAParserEngine.prototype = new UAParserItem();
-
- function UAParserOS (ua, osMap, uach) {
- UAParserItem.call(this, [
- ua,
- uach,
- UA_OS,
- osMap,
- [NAME, VERSION],
- [VERSION],
- ' ?os$',
- [NAME, VERSION]
- ]);
- this.parse();
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
- }
- }
- UAParserOS.prototype = new UAParserItem();
- UAParserOS.prototype.parseCH = function () {
- var osName = this.uaCH[PLATFORM];
- if(osName) {
- var osVersion = this.uaCH[PLATFORMVER];
- osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
- this.set(NAME, osName)
- .set(VERSION, osVersion);
- }
+ UAParserItem.prototype.set = function (prop, val) {
+ this.data[prop] = val;
return this;
};
- function UAParserResult (ua, resMap, uach) {
- UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]);
- }
- UAParserResult.prototype = new UAParserItem();
-
function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) {
@@ -1161,52 +1130,29 @@ const window = undefined;
HTTP_UACH = new UAParserDataCH(headers, true),
regexMap = extensions ?
- extend(regexes, extensions) :
- regexes;
-
- // public methods
- this.getBrowser = function () {
- return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get();
- };
-
- this.getCPU = function () {
- return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get();
- };
-
- this.getDevice = function () {
- return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get();
- };
-
- this.getEngine = function () {
- return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get();
- };
-
- this.getOS = function () {
- return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get();
- };
+ extend(defaultRegexes, extensions) :
+ defaultRegexes,
- this.getResult = function () {
- return new UAParserResult(userAgent, regexMap, HTTP_UACH)
- .set('ua', userAgent)
- .set('ua_ch', HTTP_UACH)
- .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();
- };
-
- this.getUA = function () {
- return userAgent;
- };
-
- this.setUA = function (ua) {
- userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
- return this;
- };
+ createUAParserItemFunc = function (itemType) {
+ return function () {
+ return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get();
+ };
+ };
- this.setUA(userAgent);
+ // public methods
+ assignFromEntries.call(this, [
+ ['getBrowser', createUAParserItemFunc(UA_BROWSER)],
+ ['getCPU', createUAParserItemFunc(UA_CPU)],
+ ['getDevice', createUAParserItemFunc(UA_DEVICE)],
+ ['getEngine', createUAParserItemFunc(UA_ENGINE)],
+ ['getOS', createUAParserItemFunc(UA_OS)],
+ ['getResult', createUAParserItemFunc(UA_RESULT)],
+ ['getUA', function () { return userAgent; }],
+ ['setUA', function (ua) {
+ userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
+ return this;
+ }]
+ ]).setUA(userAgent);
return this;
}
diff --git a/test/es6-test.mjs b/test/es6-test.mjs
index 4d8495872..442170cf9 100644
--- a/test/es6-test.mjs
+++ b/test/es6-test.mjs
@@ -1,4 +1,4 @@
-import { UAParser } from '../dist/ua-parser.mjs'
+import { UAParser } from '../src/ua-parser.mjs'
import * as assert from 'assert'
describe('Returns', () => {
From af65fd69600ec613bfb4709398302066cead4f66 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 28 Mar 2023 06:23:19 +0700
Subject: [PATCH 055/388] Fix #300 : Provide enums - initial work
---
package.json | 8 +++-
readme.md | 8 ++++
src/ua-parser-enum.js | 100 +++++++++++++++++++++++++++++++++++++++
src/ua-parser-enum.mjs | 103 +++++++++++++++++++++++++++++++++++++++++
test/es6-test.mjs | 14 +++++-
5 files changed, 230 insertions(+), 3 deletions(-)
create mode 100644 src/ua-parser-enum.js
create mode 100644 src/ua-parser-enum.mjs
diff --git a/package.json b/package.json
index b00b5c5c8..86173847e 100644
--- a/package.json
+++ b/package.json
@@ -144,15 +144,21 @@
"main": "src/ua-parser.js",
"module": "src/ua-parser.mjs",
"exports": {
+ "." : {
"require": "./src/ua-parser.js",
"import": "./src/ua-parser.mjs"
+ },
+ "./enums" : {
+ "require": "./src/ua-parser-enum.js",
+ "import": "./src/ua-parser-enum.mjs"
+ }
},
"files": [
"dist",
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"",
+ "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// Source file: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
"test": "jshint src/ua-parser.js && mocha -R nyan test",
"test-ci": "jshint src/ua-parser.js && mocha -R spec test",
"verup": "node ./node_modules/verup",
diff --git a/readme.md b/readme.md
index 7ad187291..071593de8 100644
--- a/readme.md
+++ b/readme.md
@@ -477,6 +477,14 @@ console.log('Server running at http://127.0.0.1:1337/');
```js
import { UAParser } from 'ua-parser-js';
+import { CPUArch, DeviceType } from 'ua-parser-js/enums';
+
+const { cpu, device } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900');
+
+console.log(browser.name) // Maemo Browser
+console.log(cpu.is(CPUArch.ARM)) // true
+console.log(device.is(DeviceType.MOBILE)) // true
+console.log(device.model) // N900
```
## Using TypeScript
diff --git a/src/ua-parser-enum.js b/src/ua-parser-enum.js
new file mode 100644
index 000000000..63762525a
--- /dev/null
+++ b/src/ua-parser-enum.js
@@ -0,0 +1,100 @@
+///////////////////////////////////////////////
+/* Enums for UAParser.js v2.0
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+//////////////////////////////////////////////
+
+const BrowserName = Object.freeze({
+ CHROME : 'Chrome',
+ EDGE : 'Edge',
+ SAFARI : 'Safari',
+ FIREFOX : 'Firefox',
+ OPERA : 'Opera',
+ MOBILE_CHROME : 'Mobile Chrome',
+ MOBILE_SAFARI : 'Mobile Safari',
+ MOBILE_FIREFOX : 'Mobile Firefox',
+ ANDROID_BROWSER : 'Android Browser'
+
+ // TODO : test!
+});
+
+const CPUArch = Object.freeze({
+ IA32 : 'ia32',
+ AMD64 : 'amd64',
+ IA64 : 'ia64',
+ ARM : 'arm',
+ ARM64 : 'arm64',
+ ARMHF : 'armhf',
+ _68K : '68k',
+ AVR : 'avr',
+ IRIX : 'irix',
+ IRIX64 : 'irix64',
+ MIPS : 'mips',
+ MIPS64 : 'mips64',
+ PPC : 'ppc',
+ SPARC : 'sparc',
+ SPARC64 : 'sparc64'
+});
+
+const DeviceType = Object.freeze({
+ MOBILE : 'mobile',
+ TABLET : 'tablet',
+ SMARTTV : 'smarttv',
+ CONSOLE : 'console',
+ WEARABLE: 'wearable',
+ EMBEDDED: 'embedded'
+});
+
+const DeviceVendor = Object.freeze({
+ APPLE : 'Apple',
+ SAMSUNG : 'Samsung',
+ HUAWEI : 'Huawei',
+ XIAOMI : 'Xiaomi',
+ OPPO : 'OPPO',
+ VIVO : 'Vivo',
+ REALME : 'Realme',
+ LENOVO : 'Lenovo',
+ LG : 'LG'
+
+ // TODO : test!
+});
+
+const EngineName = Object.freeze({
+ AMAYA : 'Amaya',
+ BLINK : 'Blink',
+ EDGEHTML: 'EdgeHTML',
+ FLOW : 'Flow',
+ GECKO : 'Gecko',
+ GOANNA : 'Goanna',
+ ICAB : 'iCab',
+ KHTML : 'KHTML',
+ LINKS : 'Links',
+ LYNX : 'Lynx',
+ NETFRONT: 'NetFront',
+ NETSURF : 'NetSurf',
+ PRESTO : 'Presto',
+ TASMAN : 'Tasman',
+ TRIDENT : 'Trident',
+ W3M : 'w3m',
+ WEBKIT : 'WebKit'
+});
+
+const OSName = Object.freeze({
+ WINDOWS : 'Windows',
+ LINUX : 'Linux',
+ MACOS : 'macOS',
+ IOS : 'iOS',
+ ANDROID : 'Android'
+
+ // TODO : test!
+});
+
+module.exports = {
+ BrowserName,
+ CPUArch,
+ DeviceType,
+ DeviceVendor,
+ EngineName,
+ OSName
+}
\ No newline at end of file
diff --git a/src/ua-parser-enum.mjs b/src/ua-parser-enum.mjs
new file mode 100644
index 000000000..74e7537aa
--- /dev/null
+++ b/src/ua-parser-enum.mjs
@@ -0,0 +1,103 @@
+// Generated ESM version of UAParser.js enums
+// Source file: /src/ua-parser-enum.js
+
+///////////////////////////////////////////////
+/* Enums for UAParser.js v2.0
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+//////////////////////////////////////////////
+
+const BrowserName = Object.freeze({
+ CHROME : 'Chrome',
+ EDGE : 'Edge',
+ SAFARI : 'Safari',
+ FIREFOX : 'Firefox',
+ OPERA : 'Opera',
+ MOBILE_CHROME : 'Mobile Chrome',
+ MOBILE_SAFARI : 'Mobile Safari',
+ MOBILE_FIREFOX : 'Mobile Firefox',
+ ANDROID_BROWSER : 'Android Browser'
+
+ // TODO : test!
+});
+
+const CPUArch = Object.freeze({
+ IA32 : 'ia32',
+ AMD64 : 'amd64',
+ IA64 : 'ia64',
+ ARM : 'arm',
+ ARM64 : 'arm64',
+ ARMHF : 'armhf',
+ _68K : '68k',
+ AVR : 'avr',
+ IRIX : 'irix',
+ IRIX64 : 'irix64',
+ MIPS : 'mips',
+ MIPS64 : 'mips64',
+ PPC : 'ppc',
+ SPARC : 'sparc',
+ SPARC64 : 'sparc64'
+});
+
+const DeviceType = Object.freeze({
+ MOBILE : 'mobile',
+ TABLET : 'tablet',
+ SMARTTV : 'smarttv',
+ CONSOLE : 'console',
+ WEARABLE: 'wearable',
+ EMBEDDED: 'embedded'
+});
+
+const DeviceVendor = Object.freeze({
+ APPLE : 'Apple',
+ SAMSUNG : 'Samsung',
+ HUAWEI : 'Huawei',
+ XIAOMI : 'Xiaomi',
+ OPPO : 'OPPO',
+ VIVO : 'Vivo',
+ REALME : 'Realme',
+ LENOVO : 'Lenovo',
+ LG : 'LG'
+
+ // TODO : test!
+});
+
+const EngineName = Object.freeze({
+ AMAYA : 'Amaya',
+ BLINK : 'Blink',
+ EDGEHTML: 'EdgeHTML',
+ FLOW : 'Flow',
+ GECKO : 'Gecko',
+ GOANNA : 'Goanna',
+ ICAB : 'iCab',
+ KHTML : 'KHTML',
+ LINKS : 'Links',
+ LYNX : 'Lynx',
+ NETFRONT: 'NetFront',
+ NETSURF : 'NetSurf',
+ PRESTO : 'Presto',
+ TASMAN : 'Tasman',
+ TRIDENT : 'Trident',
+ W3M : 'w3m',
+ WEBKIT : 'WebKit'
+});
+
+const OSName = Object.freeze({
+ WINDOWS : 'Windows',
+ LINUX : 'Linux',
+ MACOS : 'macOS',
+ IOS : 'iOS',
+ ANDROID : 'Android'
+
+ // TODO : test!
+});
+
+export {
+ BrowserName,
+ CPUArch,
+ DeviceType,
+ DeviceVendor,
+ EngineName,
+ OSName
+}
\ No newline at end of file
diff --git a/test/es6-test.mjs b/test/es6-test.mjs
index 442170cf9..e3e0cbc24 100644
--- a/test/es6-test.mjs
+++ b/test/es6-test.mjs
@@ -1,5 +1,6 @@
-import { UAParser } from '../src/ua-parser.mjs'
-import * as assert from 'assert'
+import { UAParser } from 'ua-parser-js';
+import { CPUArch, DeviceType, EngineName } from 'ua-parser-js/enums';
+import * as assert from 'assert';
describe('Returns', () => {
it('getResult() should returns object', () => {
@@ -14,4 +15,13 @@ describe('Returns', () => {
os: { name: undefined, version: undefined }
});
});
+});
+
+describe('Enums', () => {
+ it('Can use enum', () => {
+ const { cpu, device, engine } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900');
+ assert.strictEqual(cpu.is(CPUArch.ARM), true);
+ assert.strictEqual(device.is(DeviceType.MOBILE), true);
+ assert.strictEqual(engine.is(EngineName.GECKO), true);
+ });
});
\ No newline at end of file
From 7a4fe6f4549fdd91412961293c6a90224c83bfba Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 28 Mar 2023 10:41:59 +0700
Subject: [PATCH 056/388] Fix #227 #237 #488 : Provide extensions - initial
work
---
package.json | 6 +++++-
src/ua-parser-extension.js | 32 ++++++++++++++++++++++++++++++++
src/ua-parser-extension.mjs | 36 ++++++++++++++++++++++++++++++++++++
test/test-extension.js | 35 +++++++++++++++++++++++++++++++++++
4 files changed, 108 insertions(+), 1 deletion(-)
create mode 100644 src/ua-parser-extension.js
create mode 100644 src/ua-parser-extension.mjs
create mode 100644 test/test-extension.js
diff --git a/package.json b/package.json
index 86173847e..e10152a63 100644
--- a/package.json
+++ b/package.json
@@ -151,6 +151,10 @@
"./enums" : {
"require": "./src/ua-parser-enum.js",
"import": "./src/ua-parser-enum.mjs"
+ },
+ "./extensions" : {
+ "require": "./src/ua-parser-extension.js",
+ "import": "./src/ua-parser-extension.mjs"
}
},
"files": [
@@ -158,7 +162,7 @@
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// Source file: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
+ "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && uglifyjs src/ua-parser-enum.js -o dist/ua-parser-enum.min.js --comments '/^ Enum/' && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/ua-parser-extension.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
"test": "jshint src/ua-parser.js && mocha -R nyan test",
"test-ci": "jshint src/ua-parser.js && mocha -R spec test",
"verup": "node ./node_modules/verup",
diff --git a/src/ua-parser-extension.js b/src/ua-parser-extension.js
new file mode 100644
index 000000000..fc4b3bc7b
--- /dev/null
+++ b/src/ua-parser-extension.js
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////
+/* Extensions for UAParser.js v2.0
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+//////////////////////////////////////////////
+
+const UAParser = require("./ua-parser")
+
+const Bots = Object.freeze({
+ browser : [
+ [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
+ ]
+});
+
+const Emails = Object.freeze({
+ browser : [
+ [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']]
+ ]
+});
+
+const Tools = Object.freeze({
+ browser : [
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']]
+ ]
+});
+
+module.exports = {
+ Bots,
+ Emails,
+ Tools
+}
\ No newline at end of file
diff --git a/src/ua-parser-extension.mjs b/src/ua-parser-extension.mjs
new file mode 100644
index 000000000..a6b4032e1
--- /dev/null
+++ b/src/ua-parser-extension.mjs
@@ -0,0 +1,36 @@
+// Generated ESM version of UAParser.js extensions
+// DO NOT EDIT THIS FILE!
+// Source: /src/ua-parser-extension.js
+
+///////////////////////////////////////////////
+/* Extensions for UAParser.js v2.0
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+//////////////////////////////////////////////
+
+const UAParser = require("./ua-parser")
+
+const Bots = Object.freeze({
+ browser : [
+ [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
+ ]
+});
+
+const Emails = Object.freeze({
+ browser : [
+ [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']]
+ ]
+});
+
+const Tools = Object.freeze({
+ browser : [
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']]
+ ]
+});
+
+export {
+ Bots,
+ Emails,
+ Tools
+}
\ No newline at end of file
diff --git a/test/test-extension.js b/test/test-extension.js
new file mode 100644
index 000000000..bbe2fd253
--- /dev/null
+++ b/test/test-extension.js
@@ -0,0 +1,35 @@
+const assert = require('assert');
+const safeRegex = require('safe-regex');
+const UAParser = require('ua-parser-js');
+const { Bots, Emails, Tools } = require('ua-parser-js/extensions');
+
+describe('Bots', () => {
+ it('Can detect bots', () => {
+ const googleBot = 'Googlebot-Video/1.0';
+ 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 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 botParser = new UAParser(Bots);
+ assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", 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"});
+
+ // try merging Bots & Tools
+ const botsAndTools = { browser : [...Bots.browser, ...Tools.browser]};
+ const botolParser = new UAParser(botsAndTools);
+ assert.deepEqual(botolParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"tool"});
+ assert.deepEqual(botolParser.setUA(facebookBot).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"});
+ });
+});
+
+// TODO : check for safe-regex
\ No newline at end of file
From a6c85d0148836b3be8b6bb93e80d199d226d41df Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 29 Mar 2023 04:52:26 +0700
Subject: [PATCH 057/388] Add new engine: LibWeb + Add new OS: SerenityOS
---
src/ua-parser-enum.js | 1 +
src/ua-parser.js | 5 +++--
test/engine-test.json | 9 +++++++++
test/os-test.json | 9 +++++++++
4 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/src/ua-parser-enum.js b/src/ua-parser-enum.js
index 63762525a..4b21b72ae 100644
--- a/src/ua-parser-enum.js
+++ b/src/ua-parser-enum.js
@@ -68,6 +68,7 @@ const EngineName = Object.freeze({
GECKO : 'Gecko',
GOANNA : 'Goanna',
ICAB : 'iCab',
+ LIBWEB : 'LibWeb',
KHTML : 'KHTML',
LINKS : 'Links',
LYNX : 'Lynx',
diff --git a/src/ua-parser.js b/src/ua-parser.js
index bdf703603..b2f9ec2b2 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -771,7 +771,8 @@
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
/ekioh(flow)\/([\w\.]+)/i, // Flow
/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
- /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab
+ /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab
+ /\b(libweb)/i
], [NAME, VERSION], [
/rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko
@@ -849,7 +850,7 @@
], [[NAME, 'Solaris'], VERSION], [
/((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
/(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX
- /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX
+ /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS
/(unix) ?([\w\.]*)/i // UNIX
], [NAME, VERSION]
]
diff --git a/test/engine-test.json b/test/engine-test.json
index 6726d27d9..e75e539cc 100644
--- a/test/engine-test.json
+++ b/test/engine-test.json
@@ -53,6 +53,15 @@
"version" : "4.5.4"
}
},
+ {
+ "desc" : "LibWeb",
+ "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb",
+ "expect" :
+ {
+ "name" : "LibWeb",
+ "version" : "undefined"
+ }
+ },
{
"desc" : "NetFront",
"ua" : "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0",
diff --git a/test/os-test.json b/test/os-test.json
index b21274cbf..3700e5339 100644
--- a/test/os-test.json
+++ b/test/os-test.json
@@ -1204,5 +1204,14 @@
"name" : "Linspire",
"version" : "1.5.0.4"
}
+ },
+ {
+ "desc" : "SerenityOS",
+ "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb",
+ "expect" :
+ {
+ "name" : "SerenityOS",
+ "version" : "undefined"
+ }
}
]
From 4af26c7a5e446c5f57353a7c22198210a97859e5 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 29 Mar 2023 05:51:35 +0700
Subject: [PATCH 058/388] Add new browser: TikTok
User-agent example:
- https://user-agents.net/s/o2A0qRAZIN
- https://explore.whatismybrowser.com/useragents/parse/241240920-android-webview-android-jny-l22-blink
---
src/ua-parser.js | 2 ++
test/browser-test.json | 27 +++++++++++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index b2f9ec2b2..9d001c178 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -365,6 +365,8 @@
], [NAME, VERSION], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
+ /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
+ ], [VERSION, [NAME, 'TikTok']], [
/headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
], [VERSION, [NAME, CHROME+' Headless']], [
diff --git a/test/browser-test.json b/test/browser-test.json
index 0b0345f22..b8f7a6e80 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -1713,6 +1713,33 @@
"major" : "10"
}
},
+ {
+ "desc" : "TikTok",
+ "ua" : "Mozilla/5.0 (Linux; Android 11; 21061119AG Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile Safari/537.36 trill_2022109040 JsSdk/1.0 NetType/MOBILE Channel/googleplay AppName/musical_ly app_version/21.9.4 ByteLocale/ru-RU ByteFullLocale/ru-RU Region/KG BytedanceWebview/d8a21c6",
+ "expect" : {
+ "name" : "TikTok",
+ "version": "21.9.4",
+ "major" : "21"
+ }
+ },
+ {
+ "desc" : "TikTok",
+ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 14_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 musical_ly_21.1.0 JsSdk/2.0 NetType/4G Channel/App Store ByteLocale/ru Region/RU ByteFullLocale/ru-RU isDarkMode/1 WKWebView/1 BytedanceWebview/d8a21c6",
+ "expect" : {
+ "name" : "TikTok",
+ "version": "21.1.0",
+ "major" : "21"
+ }
+ },
+ {
+ "desc" : "TikTok",
+ "ua" : "Mozilla/5.0 (Linux; Android 10; STK-LX1 Build/HONORSTK-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile Safari/537.36 musical_ly_2022803040 JsSdk/1.0 NetType/WIFI Channel/huaweiadsglobal_int AppName/musical_ly app_version/28.3.4 ByteLocale/en ByteFullLocale/en Region/IQ Spark/1.2.7-alpha.8 AppVersion/28.3.4 PIA/1.5.11 BytedanceWebview/d8a21c6",
+ "expect" : {
+ "name" : "TikTok",
+ "version": "28.3.4",
+ "major" : "28"
+ }
+ },
{
"desc" : "Chrome Mobile",
"ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
From 894512c72ffa3ce6e0969f3d4a67740d88b82475 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 29 Mar 2023 10:02:56 +0700
Subject: [PATCH 059/388] Move some low-usage devices to extension as
ExtraDevices
---
src/ua-parser-extension.js | 97 ++++++++++-
src/ua-parser.js | 57 +------
test/device-test.json | 342 -------------------------------------
test/test-extension.js | 1 +
test/test-extension.json | 342 +++++++++++++++++++++++++++++++++++++
test/test.js | 14 +-
6 files changed, 444 insertions(+), 409 deletions(-)
create mode 100644 test/test-extension.json
diff --git a/src/ua-parser-extension.js b/src/ua-parser-extension.js
index fc4b3bc7b..c9c64bd6c 100644
--- a/src/ua-parser-extension.js
+++ b/src/ua-parser-extension.js
@@ -5,28 +5,117 @@
MIT License */
//////////////////////////////////////////////
-const UAParser = require("./ua-parser")
+const UAParser = require('ua-parser-js');
+const MODEL = 'model';
+const NAME = 'name';
+const TYPE = 'type';
+const VENDOR = 'vendor';
+const VERSION = 'version';
+const MOBILE = 'mobile';
+const TABLET = 'tablet';
const Bots = Object.freeze({
browser : [
- [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
+ // Googlebot / BingBot / MSNBot / FacebookBot
+ [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
+ ]
+});
+
+const ExtraDevices = Object.freeze({
+ device : [
+ [
+ /(nook)[\w ]+build\/(\w+)/i, // Nook
+ /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
+ /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
+ /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
+ /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
+ /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
+ ], [VENDOR, MODEL, [TYPE, TABLET]], [
+
+ /(u304aa)/i // AT&T
+ ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
+
+ /\bsie-(\w*)/i // Siemens
+ ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
+
+ /\b(rct\w+) b/i // RCA Tablets
+ ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
+
+ /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
+ ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
+
+ /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
+ ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
+
+ /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
+ ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
+
+ /\b(tm\d{3}\w+) b/i
+ ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
+
+ /\b(k88) b/i // ZTE K Series Tablet
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
+
+ /\b(nx\d{3}j) b/i // ZTE Nubia
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
+
+ /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
+
+ /\b(zur\d{3}) b/i // Swiss ZUR Tablet
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
+
+ /\b((zeki)?tb.*\b) b/i // Zeki Tablets
+ ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
+
+ /\b([yr]\d{2}) b/i,
+ /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
+ ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [
+
+ /\b(ns-?\w{0,9}) b/i // Insignia Tablets
+ ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
+
+ /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
+ ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
+
+ /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
+ ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
+
+ /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
+ ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
+
+ /\b(ph-1) /i // Essential PH-1
+ ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
+
+ /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
+ ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
+
+ /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
+ ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
+
+ /\btu_(1491) b/i // Rotor Tablets
+ ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]
+ ]
]
});
const Emails = Object.freeze({
browser : [
- [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']]
+ // Microsoft Outlook / Thunderbird
+ [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
]
});
const Tools = Object.freeze({
browser : [
- [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']]
+ // wget / curl / lynx
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']]
]
});
module.exports = {
Bots,
+ ExtraDevices,
Emails,
Tools
}
\ No newline at end of file
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 9d001c178..7ac4aa31c 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -71,10 +71,8 @@
SAMSUNG = 'Samsung',
SHARP = 'Sharp',
SONY = 'Sony',
- SWISS = 'Swiss',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
- ZTE = 'ZTE',
PREFIX_MOBILE = 'Mobile ',
SUFFIX_BROWSER = ' Browser',
CHROME = 'Chrome',
@@ -266,10 +264,6 @@
'8.1' : 'NT 6.3',
'10' : ['NT 6.4', 'NT 10.0'],
'RT' : 'ARM'
- },
- archEquivalenceMap = {
- 'amd64' : ['x86-64', 'x64'],
- 'ia32' : ['x86']
};
//////////////
@@ -609,62 +603,13 @@
/(kobo)\s(ereader|touch)/i, // Kobo
/(archos) (gamepad2?)/i, // Archos
/(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
- /(kindle)\/([\w\.]+)/i, // Kindle
- /(nook)[\w ]+build\/(\w+)/i, // Nook
- /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
- /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
- /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
- /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
- /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
+ /(kindle)\/([\w\.]+)/i // Kindle
], [VENDOR, MODEL, [TYPE, TABLET]], [
/(surface duo)/i // Surface Duo
], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [
/droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone
], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [
- /(u304aa)/i // AT&T
- ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
- /\bsie-(\w*)/i // Siemens
- ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
- /\b(rct\w+) b/i // RCA Tablets
- ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
- /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
- ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
- /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
- ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
- /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
- ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
- /\b(tm\d{3}\w+) b/i
- ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
- /\b(k88) b/i // ZTE K Series Tablet
- ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [
- /\b(nx\d{3}j) b/i // ZTE Nubia
- ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [
- /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
- ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [
- /\b(zur\d{3}) b/i // Swiss ZUR Tablet
- ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [
- /\b((zeki)?tb.*\b) b/i // Zeki Tablets
- ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
- /\b([yr]\d{2}) b/i,
- /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
- ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [
- /\b(ns-?\w{0,9}) b/i // Insignia Tablets
- ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
- /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
- ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
- /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
- ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
- /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
- ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
- /\b(ph-1) /i // Essential PH-1
- ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
- /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
- ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
- /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
- ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
- /\btu_(1491) b/i // Rotor Tablets
- ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [
/(shield[\w ]+) b/i // Nvidia Shield Tablets
], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [
/(sprint) (\w+)/i // Sprint Phones
diff --git a/test/device-test.json b/test/device-test.json
index a7cb4b73f..1c54884c0 100644
--- a/test/device-test.json
+++ b/test/device-test.json
@@ -205,15 +205,6 @@
"type": "mobile"
}
},
- {
- "desc": "Essential PH-1",
- "ua": "Mozilla/5.0 (Linux; Android 9; PH-1 Build/PPR1.180905.036) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36",
- "expect": {
- "vendor": "Essential",
- "model": "PH-1",
- "type": "mobile"
- }
- },
{
"desc": "Fairphone 1U",
"ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; FP1U Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
@@ -2727,294 +2718,6 @@
"type": "tablet"
}
},
- {
- "desc": "RCA Voyager III Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 6.0.1; RCT6973W43 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "RCA",
- "model": "RCT6973W43",
- "type": "tablet"
- }
- },
- {
- "desc": "RCA Voyager II Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 5.0; RCT6773W22B Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "RCA",
- "model": "RCT6773W22B",
- "type": "tablet"
- }
- },
- {
- "desc": "Verizon Quanta Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.2; QMV7B Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Verizon",
- "model": "QMV7B",
- "type": "tablet"
- }
- },
- {
- "desc": "Verizon Ellipsis 8 Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 5.1.1; QTAQZ3 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Verizon",
- "model": "QTAQZ3",
- "type": "tablet"
- }
- },
- {
- "desc": "Verizon Ellipsis 8HD Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 6.0.1; QTASUN1 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Safari/537.36",
- "expect": {
- "vendor": "Verizon",
- "model": "QTASUN1",
- "type": "tablet"
- }
- },
- {
- "desc": "Dell Venue 8 Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 8 3830 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Dell",
- "model": "Venue 8 3830",
- "type": "tablet"
- }
- },
- {
- "desc": "Dell Venue 7 Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 7 3730 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Dell",
- "model": "Venue 7 3730",
- "type": "tablet"
- }
- },
- {
- "desc": "Barnes & Noble Nook HD+ Tablet",
- "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; Barnes & Noble Nook HD+ Build/JZO54K; CyanogenMod-10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
- "expect": {
- "vendor": "Barnes & Noble",
- "model": "Nook HD+",
- "type": "tablet"
- }
- },
- {
- "desc": "Barnes & Noble V400 Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36",
- "expect": {
- "vendor": "Barnes & Noble",
- "model": "V400",
- "type": "tablet"
- }
- },
- {
- "desc": "NuVision TM101A540N Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 5.1; TM101A540N Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/50.0.2661.86 Safari/537.36",
- "expect": {
- "vendor": "NuVision",
- "model": "TM101A540N",
- "type": "tablet"
- }
- },
- {
- "desc": "ZTE-Z431",
- "ua": "ZTE-Z431/1.4.0 NetFront/4.2 QTV5.1 Profile/MIDP-2.1 Configuration/CLDC-1.1",
- "expect": {
- "vendor": "ZTE",
- "model": "Z431",
- "type": "mobile"
- }
- },
- {
- "desc": "ZTE",
- "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; ZTE-Z740G Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
- "expect": {
- "vendor": "ZTE",
- "model": "Z740G",
- "type": "mobile"
- }
- },
- {
- "desc": "ZTE K Series Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 6.0.1; K88 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "ZTE",
- "model": "K88",
- "type": "tablet"
- }
- },
- {
- "desc": "ZTE Nubia Red Magic 3",
- "ua": "Mozilla/5.0 (Linux; Android 9; NX629J Build/PKQ1.190321.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/45016 Mobile Safari/537.36 MMWEBID/4064 MicroMessenger/7.0.10.1580(0x27000A34) Process/tools NetType/WIFI Language/zh_CN ABI/arm64",
- "expect": {
- "vendor": "ZTE",
- "model": "NX629J",
- "type": "mobile"
- }
- },
- {
- "desc": "ZTE Blade A5",
- "ua": "Mozilla/5.0 (Linux; Android 9; ZTE Blade A5 2019) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36",
- "expect": {
- "vendor": "ZTE",
- "model": "Blade A5 2019",
- "type": "mobile"
- }
- },
- {
- "desc": "ZTE BLADE V0730",
- "ua": "Mozilla/5.0 (Linux; Android 6.0; ZTE BLADE V0730) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36",
- "expect": {
- "vendor": "ZTE",
- "model": "BLADE V0730",
- "type": "mobile"
- }
- },
- {
- "desc": "ZTE B2017G",
- "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE B2017G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36",
- "expect": {
- "vendor": "ZTE",
- "model": "B2017G",
- "type": "mobile"
- }
- },
- {
- "desc": "Swizz GEN610",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.2; GEN610 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36",
- "expect": {
- "vendor": "Swiss",
- "model": "GEN610",
- "type": "mobile"
- }
- },
- {
- "desc": "Swizz ZUR700",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ZUR700 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Safari/537.36",
- "expect": {
- "vendor": "Swiss",
- "model": "ZUR700",
- "type": "tablet"
- }
- },
- {
- "desc": "Zeki TB782b Tablet",
- "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-US; TB782B Build/IMM76D) AppleWebKit/534.31 (KHTML, like Gecko) UCBrowser/9.0.2.299 U3/0.8.0 Mobile Safari/534.31",
- "expect": {
- "vendor": "Zeki",
- "model": "TB782B",
- "type": "tablet"
- }
- },
- {
- "desc": "Dragon Touch Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 4.0.4; DT9138B Build/IMM76D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Mobile Safari/537.36",
- "expect": {
- "vendor": "Dragon Touch",
- "model": "9138B",
- "type": "tablet"
- }
- },
- {
- "desc": "Insignia Tablet",
- "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; NS-P08A7100 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Insignia",
- "model": "NS-P08A7100",
- "type": "tablet"
- }
- },
- {
- "desc": "Voice Xtreme V75",
- "ua": "Mozilla/5.0 (Linux; U; Android 4.2.1; en-us; V75 Build/JOP40D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
- "expect": {
- "vendor": "Voice",
- "model": "V75",
- "type": "mobile"
- }
- },
- {
- "desc": "LvTel V11",
- "ua": "Mozilla/5.0 (Linux; Android 5.1.1; V11 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36",
- "expect": {
- "vendor": "LvTel",
- "model": "V11",
- "type": "mobile"
- }
- },
- {
- "desc": "Envizen Tablet V100MD",
- "ua": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; V100MD Build/V100MD.20130816) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
- "expect": {
- "vendor": "Envizen",
- "model": "V100MD",
- "type": "tablet"
- }
- },
- {
- "desc": "Rotor Tablet",
- "ua": "mozilla/5.0 (linux; android 5.0.1; tu_1491 build/lrx22c) applewebkit/537.36 (khtml, like gecko) chrome/43.0.2357.93 safari/537.36",
- "expect": {
- "vendor": "Rotor",
- "model": "1491",
- "type": "tablet"
- }
- },
- {
- "desc": "MachSpeed Tablets",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Trio 7.85 vQ Build/Trio_7.85_vQ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36",
- "expect": {
- "vendor": "MachSpeed",
- "model": "Trio 7.85 vQ",
- "type": "tablet"
- }
- },
- {
- "desc": "Trinity Tablets",
- "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Trinity T101 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Safari/537.36",
- "expect": {
- "vendor": "Trinity",
- "model": "T101",
- "type": "tablet"
- }
- },
- {
- "desc": "NextBook Next7",
- "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; Next7P12 Build/IMM76I) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
- "expect": {
- "vendor": "NextBook",
- "model": "Next7P12",
- "type": "tablet"
- }
- },
- {
- "desc": "NextBook Tablets",
- "ua": "Mozilla/5.0 (Linux; Android 5.0; NXA8QC116 Build/LRX21V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "NextBook",
- "model": "NXA8QC116",
- "type": "tablet"
- }
- },
- {
- "desc": "Le Pan Tablets",
- "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Le Pan",
- "model": "TC802A",
- "type": "tablet"
- }
- },
- {
- "desc": "Le Pan Tablets",
- "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Le Pan",
- "model": "TC802A",
- "type": "tablet"
- }
- },
{
"desc": "Amazon Alexa Echo Show",
"ua": "AlexaWebMediaPlayer/1.0.200641.0 (Linux;Android 5.1.1)",
@@ -3078,15 +2781,6 @@
"type": "smarttv"
}
},
- {
- "desc": "Gigaset Tablet",
- "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Gigaset QV830 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
- "expect": {
- "vendor": "Gigaset",
- "model": "QV830",
- "type": "tablet"
- }
- },
{
"desc": "Amazon Fire 7",
"ua": "Mozilla/5.0 (Linux; Android 5.1.1; KFAUWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/80.5.3 like Chrome/80.0.3987.162 Safari/537.36",
@@ -3096,42 +2790,6 @@
"type": "tablet"
}
},
- {
- "desc": "AT&T Radiant Core U304AA",
- "ua": "Dalvik/2.1.0 (Linux; U; Android 9; U304AA Build/P00610)",
- "expect": {
- "vendor": "AT&T",
- "model": "U304AA",
- "type": "mobile"
- }
- },
- {
- "desc": "Vodafone Smart Tab 4G",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.4; Vodafone Smart Tab 4G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
- "expect": {
- "vendor": "Vodafone",
- "model": "Smart Tab 4G",
- "type": "tablet"
- }
- },
- {
- "desc": "Vodafone Smart ultra 6",
- "ua": "Mozilla/5.0 (Linux; Android 5.0.2; Vodafone Smart ultra 6 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36",
- "expect": {
- "vendor": "Vodafone",
- "model": "Smart ultra 6",
- "type": "tablet"
- }
- },
- {
- "desc": "4ife 4K Smart TV Box",
- "ua": "Mozilla/5.0 (Linux; Android 4.4.2; 4ife 4K Smart TV Box Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 Vinebre",
- "expect": {
- "vendor": "undefined",
- "model": "undefined",
- "type": "smarttv"
- }
- },
{
"desc": "FaceBook Mobile App",
"ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]",
diff --git a/test/test-extension.js b/test/test-extension.js
index bbe2fd253..250ee6442 100644
--- a/test/test-extension.js
+++ b/test/test-extension.js
@@ -32,4 +32,5 @@ describe('Bots', () => {
});
});
+// TODO : move test spec to JSON file
// TODO : check for safe-regex
\ No newline at end of file
diff --git a/test/test-extension.json b/test/test-extension.json
new file mode 100644
index 000000000..174c4adbe
--- /dev/null
+++ b/test/test-extension.json
@@ -0,0 +1,342 @@
+[{
+ "desc": "Essential PH-1",
+ "ua": "Mozilla/5.0 (Linux; Android 9; PH-1 Build/PPR1.180905.036) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Essential",
+ "model": "PH-1",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "Gigaset Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Gigaset QV830 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Gigaset",
+ "model": "QV830",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "RCA Voyager III Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 6.0.1; RCT6973W43 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "RCA",
+ "model": "RCT6973W43",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "RCA Voyager II Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 5.0; RCT6773W22B Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "RCA",
+ "model": "RCT6773W22B",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Verizon Quanta Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; QMV7B Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Verizon",
+ "model": "QMV7B",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Verizon Ellipsis 8 Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 5.1.1; QTAQZ3 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Verizon",
+ "model": "QTAQZ3",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Verizon Ellipsis 8HD Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 6.0.1; QTASUN1 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Safari/537.36",
+ "expect": {
+ "vendor": "Verizon",
+ "model": "QTASUN1",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Dell Venue 8 Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 8 3830 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Dell",
+ "model": "Venue 8 3830",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Dell Venue 7 Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 7 3730 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Dell",
+ "model": "Venue 7 3730",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Barnes & Noble Nook HD+ Tablet",
+ "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; Barnes & Noble Nook HD+ Build/JZO54K; CyanogenMod-10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ "expect": {
+ "vendor": "Barnes & Noble",
+ "model": "Nook HD+",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Barnes & Noble V400 Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36",
+ "expect": {
+ "vendor": "Barnes & Noble",
+ "model": "V400",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "NuVision TM101A540N Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 5.1; TM101A540N Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/50.0.2661.86 Safari/537.36",
+ "expect": {
+ "vendor": "NuVision",
+ "model": "TM101A540N",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "ZTE-Z431",
+ "ua": "ZTE-Z431/1.4.0 NetFront/4.2 QTV5.1 Profile/MIDP-2.1 Configuration/CLDC-1.1",
+ "expect": {
+ "vendor": "ZTE",
+ "model": "Z431",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "ZTE",
+ "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; ZTE-Z740G Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ "expect": {
+ "vendor": "ZTE",
+ "model": "Z740G",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "ZTE K Series Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 6.0.1; K88 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "ZTE",
+ "model": "K88",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "ZTE Nubia Red Magic 3",
+ "ua": "Mozilla/5.0 (Linux; Android 9; NX629J Build/PKQ1.190321.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/45016 Mobile Safari/537.36 MMWEBID/4064 MicroMessenger/7.0.10.1580(0x27000A34) Process/tools NetType/WIFI Language/zh_CN ABI/arm64",
+ "expect": {
+ "vendor": "ZTE",
+ "model": "NX629J",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "ZTE Blade A5",
+ "ua": "Mozilla/5.0 (Linux; Android 9; ZTE Blade A5 2019) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "ZTE",
+ "model": "Blade A5 2019",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "ZTE BLADE V0730",
+ "ua": "Mozilla/5.0 (Linux; Android 6.0; ZTE BLADE V0730) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "ZTE",
+ "model": "BLADE V0730",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "ZTE B2017G",
+ "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE B2017G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "ZTE",
+ "model": "B2017G",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "Swizz GEN610",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; GEN610 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Swiss",
+ "model": "GEN610",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "Swizz ZUR700",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ZUR700 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Safari/537.36",
+ "expect": {
+ "vendor": "Swiss",
+ "model": "ZUR700",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Zeki TB782b Tablet",
+ "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-US; TB782B Build/IMM76D) AppleWebKit/534.31 (KHTML, like Gecko) UCBrowser/9.0.2.299 U3/0.8.0 Mobile Safari/534.31",
+ "expect": {
+ "vendor": "Zeki",
+ "model": "TB782B",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Dragon Touch Tablet",
+ "ua": "Mozilla/5.0 (Linux; Android 4.0.4; DT9138B Build/IMM76D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Dragon Touch",
+ "model": "9138B",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Insignia Tablet",
+ "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; NS-P08A7100 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Insignia",
+ "model": "NS-P08A7100",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Voice Xtreme V75",
+ "ua": "Mozilla/5.0 (Linux; U; Android 4.2.1; en-us; V75 Build/JOP40D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ "expect": {
+ "vendor": "Voice",
+ "model": "V75",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "LvTel V11",
+ "ua": "Mozilla/5.0 (Linux; Android 5.1.1; V11 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36",
+ "expect": {
+ "vendor": "LvTel",
+ "model": "V11",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "Envizen Tablet V100MD",
+ "ua": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; V100MD Build/V100MD.20130816) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
+ "expect": {
+ "vendor": "Envizen",
+ "model": "V100MD",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Rotor Tablet",
+ "ua": "mozilla/5.0 (linux; android 5.0.1; tu_1491 build/lrx22c) applewebkit/537.36 (khtml, like gecko) chrome/43.0.2357.93 safari/537.36",
+ "expect": {
+ "vendor": "Rotor",
+ "model": "1491",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "MachSpeed Tablets",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Trio 7.85 vQ Build/Trio_7.85_vQ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36",
+ "expect": {
+ "vendor": "MachSpeed",
+ "model": "Trio 7.85 vQ",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Trinity Tablets",
+ "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Trinity T101 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Safari/537.36",
+ "expect": {
+ "vendor": "Trinity",
+ "model": "T101",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "NextBook Next7",
+ "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; Next7P12 Build/IMM76I) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
+ "expect": {
+ "vendor": "NextBook",
+ "model": "Next7P12",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "NextBook Tablets",
+ "ua": "Mozilla/5.0 (Linux; Android 5.0; NXA8QC116 Build/LRX21V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "NextBook",
+ "model": "NXA8QC116",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Le Pan Tablets",
+ "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Le Pan",
+ "model": "TC802A",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Le Pan Tablets",
+ "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
+ "expect": {
+ "vendor": "Le Pan",
+ "model": "TC802A",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "AT&T Radiant Core U304AA",
+ "ua": "Dalvik/2.1.0 (Linux; U; Android 9; U304AA Build/P00610)",
+ "expect": {
+ "vendor": "AT&T",
+ "model": "U304AA",
+ "type": "mobile"
+ }
+},
+{
+ "desc": "Vodafone Smart Tab 4G",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.4; Vodafone Smart Tab 4G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
+ "expect": {
+ "vendor": "Vodafone",
+ "model": "Smart Tab 4G",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "Vodafone Smart ultra 6",
+ "ua": "Mozilla/5.0 (Linux; Android 5.0.2; Vodafone Smart ultra 6 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Vodafone",
+ "model": "Smart ultra 6",
+ "type": "tablet"
+ }
+},
+{
+ "desc": "4ife 4K Smart TV Box",
+ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; 4ife 4K Smart TV Box Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 Vinebre",
+ "expect": {
+ "vendor": "undefined",
+ "model": "undefined",
+ "type": "smarttv"
+ }
+}]
\ No newline at end of file
diff --git a/test/test.js b/test/test.js
index 8077e82da..a7dbeea43 100644
--- a/test/test.js
+++ b/test/test.js
@@ -82,7 +82,7 @@ describe('Returns', function () {
assert.deepEqual(new UAParser('').getResult(),
{
ua : '',
- ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined },
+ //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 },
cpu: { architecture: undefined },
device: { vendor: undefined, model: undefined, type: undefined },
@@ -362,7 +362,7 @@ describe('Map UA-CH headers', function () {
let engine = new UAParser(headers).getEngine().withClientHints();
let os = new UAParser(headers).getOS().withClientHints();
- let ua_ch = {
+/* let ua_ch = {
"architecture": "ARM",
"bitness": "64",
"brands": [
@@ -397,11 +397,11 @@ describe('Map UA-CH headers', function () {
"model": "Pixel 99",
"platform": "Windows",
"platformVersion": "13"
- };
+ };*/
it('Can read from client-hints headers using `withClientHints()`', function () {
- assert.deepEqual(uap.ua_ch, ua_ch);
+ //assert.deepEqual(uap.ua_ch, ua_ch);
assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "93.0.1.2");
@@ -436,7 +436,7 @@ describe('Map UA-CH headers', function () {
engine = new UAParser(headers).getEngine();
os = new UAParser(headers).getOS();
- assert.deepEqual(uap.ua_ch, ua_ch);
+ //assert.deepEqual(uap.ua_ch, ua_ch);
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "110.0.0.0");
assert.strictEqual(uap.browser.major, "110");
@@ -459,7 +459,7 @@ describe('Map UA-CH headers', function () {
uap = UAParser(headers2).withClientHints();
- ua_ch = {
+/* ua_ch = {
"architecture": undefined,
"bitness": undefined,
"brands": undefined,
@@ -470,7 +470,7 @@ describe('Map UA-CH headers', function () {
"platformVersion": undefined
};
- assert.deepEqual(uap.ua_ch, ua_ch);
+ assert.deepEqual(uap.ua_ch, ua_ch);*/
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "110.0.0.0");
assert.strictEqual(uap.browser.major, "110");
From 30de983043dc44e24980c4983bb4f0f250084226 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 29 Mar 2023 10:44:37 +0700
Subject: [PATCH 060/388] Alpha release of v2.0
---
bower.json | 2 +-
changelog.md | 68 +++++++----
dist/ua-parser.min.js | 6 +-
dist/ua-parser.pack.js | 6 +-
package.js | 2 +-
package.json | 20 ++--
readme.md | 109 ++++++++++--------
src/{ => enum}/ua-parser-enum.js | 2 +-
src/{ => enum}/ua-parser-enum.mjs | 6 +-
src/{ => extension}/ua-parser-extension.js | 3 +-
src/extension/ua-parser-extension.mjs | 124 +++++++++++++++++++++
src/ua-parser-extension.mjs | 36 ------
src/ua-parser.js | 6 +-
src/ua-parser.mjs | 73 ++----------
test/cpu-test.json | 8 ++
test/{es6-test.mjs => test-es6.mjs} | 2 +-
16 files changed, 279 insertions(+), 194 deletions(-)
rename src/{ => enum}/ua-parser-enum.js (98%)
rename src/{ => enum}/ua-parser-enum.mjs (94%)
rename src/{ => extension}/ua-parser-extension.js (98%)
create mode 100644 src/extension/ua-parser-extension.mjs
delete mode 100644 src/ua-parser-extension.mjs
rename test/{es6-test.mjs => test-es6.mjs} (84%)
diff --git a/bower.json b/bower.json
index c59773a0e..05b8579f8 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "ua-parser-js",
- "version": "0.7.33",
+ "version": "2.0.0-alpha.1",
"authors": [
"Faisal Salman "
],
diff --git a/changelog.md b/changelog.md
index addaff4d3..a4aa6f1e3 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,27 +1,42 @@
# UAParser.js Changelog
+# Version 2.0.0-alpha.1
+- Breaking changes:
+ - Browser detection on mobile device: `Chrome => Mobile Chrome`, `Firefox => Mobile Firefox`
+ - OS detection: `Mac OS => macOS`, `Chromium OS => Chrome OS`
+- Add some new methods in result object:
+ - Add support for client hints: `withClientHints()`
+ - Utility for easy comparison: `is()`
+ - Utility to print full-name: `toString()`
+- Add support for ES module `import { UAParser } from 'ua-parser-js'`
+- Provide Enums `'ua-parser-js/enums'`
+- Provide Extensions `'ua-parser-js/extensions'`
+- Add new browser: Heytap, TikTok
+- Add new engine: LibWeb
+- Add new OS: SerenityOS
+- Improve browser detection: Yandex
+- Improve device detection: iPhone, Amazon Echo
+- Improve OS detection: iOS
+
# Version 0.7 / 1.0
Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion.
-## Version 0.7.30 / 1.0.1
+## Version 0.7.34 / 1.0.34
+- Fix Sharp Mobile detected as Huawei Tablet
+- Fix IE8 bug
+- Add new devices : Kobo e-Reader, Apple Watch, and some new SmartTV devices
+- Add new OS : watchOS
+- Improve browser detection : Kakao, Naver, Brave
+- Improve device detection : Oculus, iPad
+- Improve OS detection : Chrome OS
+- Using navigator.userAgentData as fallback for device.type & os.name
-- Add new browser : Obigo, UP.Browser, Klar
-- Add new device : Oculus, Roku
-- Add new OS: Maemo, HP-UX, Android-x86, Deepin, elementary OS, GhostBSD, Linspire, Manjaro, Sabayon
-- Improve detection for Sony Xperia 1ii, LG Android TV, and some more devices
-- Improve detection for ARM64 CPU
-- Improve detection for Windows Mobile, Netscape, Mac on PowerPC
-- Categorize PDA as mobile
-- Fix Sharp devices misjudged as Huawei
-- Fix trailing comma for ES3 compatibility
-- Some code refactor
-
-## Version 0.7.31 / 1.0.2
+## Version 0.7.33 / 1.0.33
-- Fix OPPO Reno A5 incorrect detection
-- Fix TypeError Bug
-- Use AST to extract regexes and verify them with safe-regex
+- Add new browser : Cobalt
+- Identify Macintosh as an Apple device
+- Fix ReDoS vulnerability
## Version 0.7.32 / 1.0.32
@@ -38,11 +53,24 @@ Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://g
- Fix included commas in Safari / Mobile Safari version
- Increase UA_MAX_LENGTH to 350
-## Version 0.7.33 / 1.0.33
+## Version 0.7.31 / 1.0.2
-- Add new browser : Cobalt
-- Identify Macintosh as an Apple device
-- Fix ReDoS vulnerability
+- Fix OPPO Reno A5 incorrect detection
+- Fix TypeError Bug
+- Use AST to extract regexes and verify them with safe-regex
+
+## Version 0.7.30 / 1.0.1
+
+- Add new browser : Obigo, UP.Browser, Klar
+- Add new device : Oculus, Roku
+- Add new OS: Maemo, HP-UX, Android-x86, Deepin, elementary OS, GhostBSD, Linspire, Manjaro, Sabayon
+- Improve detection for Sony Xperia 1ii, LG Android TV, and some more devices
+- Improve detection for ARM64 CPU
+- Improve detection for Windows Mobile, Netscape, Mac on PowerPC
+- Categorize PDA as mobile
+- Fix Sharp devices misjudged as Huawei
+- Fix trailing comma for ES3 compatibility
+- Some code refactor
# Version 0.8
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index 71c56fb0c..2936c2e63 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
-/* UAParser.js v1.1.34
- Copyright © 2012-2021 Faisal Salman
+/* UAParser.js v2.0.0-alpha.1
+ Copyright © 2012-2023 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="0.7.32",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",UA_MAX_LENGTH=350;var AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",BROWSER="Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",OPERA="Opera",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",FACEBOOK="Facebook";var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){mergedRegexes[i]=extensions[i].concat(regexes[i])}else{mergedRegexes[i]=regexes[i]}}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;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;jUA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this};this.setUA(_ua);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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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);
+(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.1",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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=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},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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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]&&NAVIGATOR_UADATA[PLATFORM]!="Unknown"){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 9318cbcef..c5d1e9c43 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
-/* UAParser.js v1.1.34
- Copyright © 2012-2021 Faisal Salman
+/* UAParser.js v2.0.0-alpha.1
+ Copyright © 2012-2023 Faisal Salman
MIT License */
-!function(a,d){"use strict";function i(i){for(var e={},o=0;oS?L(i,S):i,this},this.setUA(o),this}Q.prototype.is=function(i){for(var e in this.propIs)if(o(this[this.propIs[e]],this.rgxIs)==o(i,this.rgxIs))return!0;return!1},Q.prototype.toString=function(){var i,e="";for(i in this.propToString)typeof this[this.propToString[i]]!==s&&(e+=(e?" ":"")+this[this.propToString[i]]);return e||s},Y.prototype=new Q([l,h],[[l],/\s?browser$/i]),J.prototype=new Q([f],[[f]]),ii.prototype=new Q([m,r],[[p,r,m]]),ei.prototype=new Q([l,h],[[l]]),oi.prototype=new Q([l,h],[[l],/\s?os$/i]),ti.VERSION="0.7.32",ti.BROWSER=i([l,h,w]),ti.CPU=i([f]),ti.DEVICE=i([r,m,p,g,v,k,x,y,_]),ti.ENGINE=ti.OS=i([l,h]),typeof exports!==s?(typeof module!==s&&module.exports&&(exports=module.exports=ti),exports.UAParser=ti):typeof define==u&&define.amd?define(function(){return ti}):typeof a!==s&&(a.UAParser=ti);var ai,ni=typeof a!==s&&(a.jQuery||a.Zepto);ni&&!ni.ua&&(ai=new ti,ni.ua=ai.getResult(),ni.ua.get=function(){return ai.getUA()},ni.ua.set=function(i){ai.setUA(i);var e,o=ai.getResult();for(e in o)ni.ua[e]=o[e]})}("object"==typeof window?window:this);
+!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,n,a,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
diff --git a/package.js b/package.js
index a3bcdb348..3f2ddf1cb 100644
--- a/package.js
+++ b/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'faisalman:ua-parser-js',
- version: '0.7.33',
+ version: '2.0.0-alpha.1',
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 e10152a63..37191dcb7 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"title": "UAParser.js",
"name": "ua-parser-js",
- "version": "0.7.33",
+ "version": "2.0.0-alpha.1",
"author": "Faisal Salman (http://faisalman.com)",
"description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment",
"keywords": [
@@ -139,22 +139,22 @@
"yuanyang ",
"Yun Young-jin ",
"Zach Bjornson "
- ],
+ ],
"type": "commonjs",
"main": "src/ua-parser.js",
"module": "src/ua-parser.mjs",
"exports": {
- "." : {
+ ".": {
"require": "./src/ua-parser.js",
"import": "./src/ua-parser.mjs"
},
- "./enums" : {
- "require": "./src/ua-parser-enum.js",
- "import": "./src/ua-parser-enum.mjs"
+ "./enums": {
+ "require": "./src/enum/ua-parser-enum.js",
+ "import": "./src/enum/ua-parser-enum.mjs"
},
- "./extensions" : {
- "require": "./src/ua-parser-extension.js",
- "import": "./src/ua-parser-extension.mjs"
+ "./extensions": {
+ "require": "./src/extension/ua-parser-extension.js",
+ "import": "./src/extension/ua-parser-extension.mjs"
}
},
"files": [
@@ -162,7 +162,7 @@
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && uglifyjs src/ua-parser-enum.js -o dist/ua-parser-enum.min.js --comments '/^ Enum/' && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/ua-parser-extension.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
+ "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
"test": "jshint src/ua-parser.js && mocha -R nyan test",
"test-ci": "jshint src/ua-parser.js && mocha -R spec test",
"verup": "node ./node_modules/verup",
diff --git a/readme.md b/readme.md
index 071593de8..ad229f322 100644
--- a/readme.md
+++ b/readme.md
@@ -43,8 +43,11 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
---
+# Version 2.0
+What's new & breaking, please read [CHANGELOG](changelog.md) before upgrading.
+
# Documentation
-### UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])
+### UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])
In the browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in' nodejs for the function to work. Usually you can find the user agent in: `request.headers["user-agent"]`.
@@ -52,7 +55,7 @@ In the browser environment you dont need to pass the user-agent string to the fu
## Constructor
When you call `UAParser` with the `new` keyword, `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string.
Like so:
-* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
+* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])`
```js
let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs
console.log(parser); // {}
@@ -65,22 +68,19 @@ console.log(parserResults);
"os" : {},
"device" : {},
"cpu" : {}
-
- // since@1.1
- ,"ua_ch" : {}
} */
```
When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
-* `UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
- * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`
+* `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])`
+ * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
## Methods
#### Methods table
The methods are self explanatory, here's a small overview on all the available methods:
* `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os:
-`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`.
+`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`.
* `getBrowser()` - returns the browser name and version.
* `getDevice()` - returns the device model, type, vendor.
@@ -93,7 +93,7 @@ The methods are self explanatory, here's a small overview on all the available m
---
* `getResult()`
- * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`
+ * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
* `getBrowser()`
* returns `{ name: '', version: '' }`
@@ -113,7 +113,7 @@ NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb,
Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris,
Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, [Mobile] Safari,
Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim,
-SlimBrowser, Swiftfox, Tesla, Tizen Browser, UCBrowser, UP.Browser, Viera,
+SlimBrowser, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser, UP.Browser, Viera,
Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ...
# 'browser.version' determined dynamically
@@ -148,8 +148,8 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE
```sh
# Possible 'engine.name'
-Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, Links, Lynx, NetFront,
-NetSurf, Presto, Tasman, Trident, w3m, WebKit
+Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, LibWeb, Links, Lynx,
+NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit
# 'engine.version' determined dynamically
```
@@ -165,8 +165,9 @@ Fuchsia, Gentoo, GhostBSD, GNU, Haiku, HarmonyOS, HP-UX, Hurd, iOS, Joli, KaiOS,
Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix,
Mint, Morph OS, NetBSD, NetRange, NetTV, Nintendo, OpenBSD, OpenVMS, OS/2, Palm,
PC-BSD, PCLinuxOS, Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS,
-RISC OS, Sabayon, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen,
-Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwalk, ...
+RISC OS, Sabayon, Sailfish, SerenityOS, Series40, Slackware, Solaris, SUSE, Symbian,
+Tizen, Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile],
+Zenwalk, ...
# 'os.version' determined dynamically
```
@@ -186,10 +187,10 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal
* set UA string to be parsed
* returns current instance
-#### * `is():boolean` utility `since@1.1`
+#### * `is():boolean` utility `since@2.0`
```js
-// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive)
+// Is just a shorthand comparison to check whether the value of specified item equals one of its properties (in a case-insensitive way)
// so that instead of write it using `==` operator like this:
let ua = UAParser();
@@ -202,15 +203,10 @@ if (device.type == "smarttv" || device.vendor == "Samsung") {}
// we can also write the comparison above into as follow:
if (device.is("mobile") && !os.is("iOS")) {}
-if (device.is("smarttv") || device.is("Samsung")) {}
+if (device.is("SmartTV") || device.is("SaMsUnG")) {}
/*
- Each properties will be checked in this particular order:
- * browser : name
- * cpu : architecture
- * device : type, model, vendor
- * engine : name
- * os : name
+ For device, properties will be checked in this particular order: type, model, vendor
*/
// Another examples:
@@ -250,7 +246,7 @@ let engine = uap.getEngine();
engine.is("Blink"); // true
```
-#### * `toString():string` utility `since@1.1`
+#### * `toString():string` utility `since@2.0`
```js
// Retrieve full-name values as a string
@@ -291,11 +287,12 @@ engine.version; // "28.0.1500.95"
engine.toString(); // "Blink 28.0.1500.95"
```
-#### * `withClientHints():Promise|Thenable` `since@1.1`
+#### * `withClientHints():Promise|Thenable` `since@2.0`
-Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`. In nodejs-environment / browser-environment with non-secure context or without client-hints support (basically anything that's not chromium-based) this will return the updated data as a `Thenable` (can be chained with `then()`).
+Recently Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`.
```js
+// client-side example
(async function () {
let ua = new UAParser();
@@ -314,12 +311,43 @@ Unlike reading user-agent data, accessing client-hints data in browser-environme
})();
```
+Along with `User-Agent` HTTP header, Chrome also sends this client-hints data by default under `Sec-CH-UA-*` HTTP headers in each request. In server-side development, you can capture this extra information by passing the `req.headers` to `UAParser()` (see examples below). When using `withClientHints()` in nodejs environment and browser without client-hints support (basically anything that's not Chromium-based) the updated data will be returned as a `Thenable` (has `then()` method).
+
+```js
+// server-side example
+
+// Suppose we got a request having these HTTP headers:
+const request = {
+ headers : {
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
+
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Galaxy S3 Marketing',
+ 'sec-ch-ua-platform' : 'Android'
+ }
+};
+
+const result1 = UAParser(request.headers); // parse only "user-agent" header
+const result2 = UAParser(request.headers).withClientHints(); // update with "sec-ch-ua" headers
+
+console.log(result1.os.name); // "Linux"
+console.log(result1.device.type); // undefined
+console.log(result1.device.model); // undefined
+
+console.log(result2.os.name); // "Android"
+console.log(result2.device.type); // "mobile"
+console.log(result2.device.model); // "Galaxy S3 Marketing"
+
+new UAParser(request.headers).getBrowser().withClientHints().then((browser) => {
+ console.log(browser.toString()); // Chrome 110.0.0.0
+});
+```
## Extending Regex
If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own.
-* `UAParser([uastring,] extensions [,headers:object(since@1.1)])`
+* `UAParser([uastring,] extensions [,headers:object(since@2.0)])`
```js
// Example:
@@ -389,18 +417,6 @@ console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "1
cpu: {
architecture: ""
}
-
- // added since@1.1:
- ,ua_ch: {
- architecture: "",
- brands: "",
- bitness: "",
- fullVersionList: "",
- mobile: "",
- model: "",
- platform: "",
- platformVersion: ""
- }
}
*/
// Default result depends on current window.navigator.userAgent value
@@ -453,7 +469,7 @@ http.createServer(function (req, res) {
// get user-agent header
var ua = uap(req.headers['user-agent']);
- /* // BEGIN since@1.1 - you can also pass client-hints data to UAParser
+ /* // BEGIN since@2.0 - you can also pass client-hints data to UAParser
// note: only works in secure context (https:// or localhost or file://)
@@ -463,7 +479,7 @@ http.createServer(function (req, res) {
var ua = uap(req.headers);
- // END since@1.1 */
+ // END since@2.0 */
// write the result as response
res.end(JSON.stringify(ua, null, ' '));
@@ -477,14 +493,13 @@ console.log('Server running at http://127.0.0.1:1337/');
```js
import { UAParser } from 'ua-parser-js';
-import { CPUArch, DeviceType } from 'ua-parser-js/enums';
-const { cpu, device } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900');
+const { browser, cpu, device } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900');
-console.log(browser.name) // Maemo Browser
-console.log(cpu.is(CPUArch.ARM)) // true
-console.log(device.is(DeviceType.MOBILE)) // true
-console.log(device.model) // N900
+console.log(browser.name); // Maemo Browser
+console.log(cpu.is('arm')); // true
+console.log(device.is('mobile')); // true
+console.log(device.model); // N900
```
## Using TypeScript
diff --git a/src/ua-parser-enum.js b/src/enum/ua-parser-enum.js
similarity index 98%
rename from src/ua-parser-enum.js
rename to src/enum/ua-parser-enum.js
index 4b21b72ae..8333eaf8f 100644
--- a/src/ua-parser-enum.js
+++ b/src/enum/ua-parser-enum.js
@@ -1,5 +1,5 @@
///////////////////////////////////////////////
-/* Enums for UAParser.js v2.0
+/* Enums for UAParser.js v2.0.0-alpha.1
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/ua-parser-enum.mjs b/src/enum/ua-parser-enum.mjs
similarity index 94%
rename from src/ua-parser-enum.mjs
rename to src/enum/ua-parser-enum.mjs
index 74e7537aa..7708532fa 100644
--- a/src/ua-parser-enum.mjs
+++ b/src/enum/ua-parser-enum.mjs
@@ -1,8 +1,9 @@
// Generated ESM version of UAParser.js enums
-// Source file: /src/ua-parser-enum.js
+// DO NOT EDIT THIS FILE!
+// Source: /src/enum/ua-parser-enum.js
///////////////////////////////////////////////
-/* Enums for UAParser.js v2.0
+/* Enums for UAParser.js v2.0.0-alpha.1
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
@@ -71,6 +72,7 @@ const EngineName = Object.freeze({
GECKO : 'Gecko',
GOANNA : 'Goanna',
ICAB : 'iCab',
+ LIBWEB : 'LibWeb',
KHTML : 'KHTML',
LINKS : 'Links',
LYNX : 'Lynx',
diff --git a/src/ua-parser-extension.js b/src/extension/ua-parser-extension.js
similarity index 98%
rename from src/ua-parser-extension.js
rename to src/extension/ua-parser-extension.js
index c9c64bd6c..dfa90ed50 100644
--- a/src/ua-parser-extension.js
+++ b/src/extension/ua-parser-extension.js
@@ -1,11 +1,10 @@
///////////////////////////////////////////////
-/* Extensions for UAParser.js v2.0
+/* Extensions for UAParser.js v2.0.0-alpha.1
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
//////////////////////////////////////////////
-const UAParser = require('ua-parser-js');
const MODEL = 'model';
const NAME = 'name';
const TYPE = 'type';
diff --git a/src/extension/ua-parser-extension.mjs b/src/extension/ua-parser-extension.mjs
new file mode 100644
index 000000000..8b645fc74
--- /dev/null
+++ b/src/extension/ua-parser-extension.mjs
@@ -0,0 +1,124 @@
+// Generated ESM version of UAParser.js extensions
+// DO NOT EDIT THIS FILE!
+// Source: /src/extension/ua-parser-extension.js
+
+///////////////////////////////////////////////
+/* Extensions for UAParser.js v2.0.0-alpha.1
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+//////////////////////////////////////////////
+
+const MODEL = 'model';
+const NAME = 'name';
+const TYPE = 'type';
+const VENDOR = 'vendor';
+const VERSION = 'version';
+const MOBILE = 'mobile';
+const TABLET = 'tablet';
+
+const Bots = Object.freeze({
+ browser : [
+ // Googlebot / BingBot / MSNBot / FacebookBot
+ [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
+ ]
+});
+
+const ExtraDevices = Object.freeze({
+ device : [
+ [
+ /(nook)[\w ]+build\/(\w+)/i, // Nook
+ /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
+ /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
+ /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
+ /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
+ /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
+ ], [VENDOR, MODEL, [TYPE, TABLET]], [
+
+ /(u304aa)/i // AT&T
+ ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
+
+ /\bsie-(\w*)/i // Siemens
+ ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
+
+ /\b(rct\w+) b/i // RCA Tablets
+ ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
+
+ /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
+ ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
+
+ /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
+ ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
+
+ /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
+ ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
+
+ /\b(tm\d{3}\w+) b/i
+ ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
+
+ /\b(k88) b/i // ZTE K Series Tablet
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
+
+ /\b(nx\d{3}j) b/i // ZTE Nubia
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
+
+ /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
+
+ /\b(zur\d{3}) b/i // Swiss ZUR Tablet
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
+
+ /\b((zeki)?tb.*\b) b/i // Zeki Tablets
+ ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
+
+ /\b([yr]\d{2}) b/i,
+ /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
+ ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [
+
+ /\b(ns-?\w{0,9}) b/i // Insignia Tablets
+ ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
+
+ /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
+ ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
+
+ /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
+ ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
+
+ /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
+ ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
+
+ /\b(ph-1) /i // Essential PH-1
+ ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
+
+ /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
+ ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
+
+ /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
+ ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
+
+ /\btu_(1491) b/i // Rotor Tablets
+ ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]
+ ]
+ ]
+});
+
+const Emails = Object.freeze({
+ browser : [
+ // Microsoft Outlook / Thunderbird
+ [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
+ ]
+});
+
+const Tools = Object.freeze({
+ browser : [
+ // wget / curl / lynx
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']]
+ ]
+});
+
+export {
+ Bots,
+ ExtraDevices,
+ Emails,
+ Tools
+}
\ No newline at end of file
diff --git a/src/ua-parser-extension.mjs b/src/ua-parser-extension.mjs
deleted file mode 100644
index a6b4032e1..000000000
--- a/src/ua-parser-extension.mjs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Generated ESM version of UAParser.js extensions
-// DO NOT EDIT THIS FILE!
-// Source: /src/ua-parser-extension.js
-
-///////////////////////////////////////////////
-/* Extensions for UAParser.js v2.0
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-//////////////////////////////////////////////
-
-const UAParser = require("./ua-parser")
-
-const Bots = Object.freeze({
- browser : [
- [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
- ]
-});
-
-const Emails = Object.freeze({
- browser : [
- [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']]
- ]
-});
-
-const Tools = Object.freeze({
- browser : [
- [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']]
- ]
-});
-
-export {
- Bots,
- Emails,
- Tools
-}
\ No newline at end of file
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 7ac4aa31c..6a82ed1ad 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////////////
-/* UAParser.js v1.1.34
+/* UAParser.js v2.0.0-alpha.1
Copyright © 2012-2023 Faisal Salman
MIT License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -17,7 +17,7 @@
/////////////
- var LIBVERSION = '1.1.34',
+ var LIBVERSION = '2.0.0-alpha.1',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -960,7 +960,6 @@
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
};
this.set('ua', ua)
- .set('ua_ch', uaCH)
.set(UA_BROWSER, createUAParserItem(UA_BROWSER))
.set(UA_CPU, createUAParserItem(UA_CPU))
.set(UA_DEVICE, createUAParserItem(UA_DEVICE))
@@ -1029,7 +1028,6 @@
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
};
this.set('ua', ua)
- .set('ua_ch', uaCH)
.set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
.set(UA_CPU, createUAParserItemWithCH(UA_CPU))
.set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
diff --git a/src/ua-parser.mjs b/src/ua-parser.mjs
index 1821d97dc..e0d93d0d8 100644
--- a/src/ua-parser.mjs
+++ b/src/ua-parser.mjs
@@ -1,10 +1,11 @@
// Generated ESM version of UAParser.js
-// Source file: /src/ua-parser.js
+// DO NOT EDIT THIS FILE!
+// Source: /src/ua-parser.js
const window = undefined;
/////////////////////////////////////////////////////////////////////////////////
-/* UAParser.js v1.1.34
+/* UAParser.js v2.0.0-alpha.1
Copyright © 2012-2023 Faisal Salman
MIT License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -20,7 +21,7 @@ const window = undefined;
/////////////
- var LIBVERSION = '1.1.34',
+ var LIBVERSION = '2.0.0-alpha.1',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -74,10 +75,8 @@ const window = undefined;
SAMSUNG = 'Samsung',
SHARP = 'Sharp',
SONY = 'Sony',
- SWISS = 'Swiss',
XIAOMI = 'Xiaomi',
ZEBRA = 'Zebra',
- ZTE = 'ZTE',
PREFIX_MOBILE = 'Mobile ',
SUFFIX_BROWSER = ' Browser',
CHROME = 'Chrome',
@@ -269,10 +268,6 @@ const window = undefined;
'8.1' : 'NT 6.3',
'10' : ['NT 6.4', 'NT 10.0'],
'RT' : 'ARM'
- },
- archEquivalenceMap = {
- 'amd64' : ['x86-64', 'x64'],
- 'ia32' : ['x86']
};
//////////////
@@ -368,6 +363,8 @@ const window = undefined;
], [NAME, VERSION], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
+ /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
+ ], [VERSION, [NAME, 'TikTok']], [
/headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
], [VERSION, [NAME, CHROME+' Headless']], [
@@ -610,62 +607,13 @@ const window = undefined;
/(kobo)\s(ereader|touch)/i, // Kobo
/(archos) (gamepad2?)/i, // Archos
/(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
- /(kindle)\/([\w\.]+)/i, // Kindle
- /(nook)[\w ]+build\/(\w+)/i, // Nook
- /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
- /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
- /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
- /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
- /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
+ /(kindle)\/([\w\.]+)/i // Kindle
], [VENDOR, MODEL, [TYPE, TABLET]], [
/(surface duo)/i // Surface Duo
], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [
/droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone
], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [
- /(u304aa)/i // AT&T
- ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
- /\bsie-(\w*)/i // Siemens
- ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
- /\b(rct\w+) b/i // RCA Tablets
- ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
- /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
- ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
- /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
- ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
- /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
- ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
- /\b(tm\d{3}\w+) b/i
- ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
- /\b(k88) b/i // ZTE K Series Tablet
- ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [
- /\b(nx\d{3}j) b/i // ZTE Nubia
- ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [
- /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
- ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [
- /\b(zur\d{3}) b/i // Swiss ZUR Tablet
- ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [
- /\b((zeki)?tb.*\b) b/i // Zeki Tablets
- ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
- /\b([yr]\d{2}) b/i,
- /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
- ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [
- /\b(ns-?\w{0,9}) b/i // Insignia Tablets
- ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
- /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
- ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
- /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
- ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
- /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
- ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
- /\b(ph-1) /i // Essential PH-1
- ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
- /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
- ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
- /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
- ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
- /\btu_(1491) b/i // Rotor Tablets
- ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [
/(shield[\w ]+) b/i // Nvidia Shield Tablets
], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [
/(sprint) (\w+)/i // Sprint Phones
@@ -774,7 +722,8 @@ const window = undefined;
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
/ekioh(flow)\/([\w\.]+)/i, // Flow
/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
- /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab
+ /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab
+ /\b(libweb)/i
], [NAME, VERSION], [
/rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko
@@ -852,7 +801,7 @@ const window = undefined;
], [[NAME, 'Solaris'], VERSION], [
/((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
/(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX
- /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX
+ /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS
/(unix) ?([\w\.]*)/i // UNIX
], [NAME, VERSION]
]
@@ -1015,7 +964,6 @@ const window = undefined;
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
};
this.set('ua', ua)
- .set('ua_ch', uaCH)
.set(UA_BROWSER, createUAParserItem(UA_BROWSER))
.set(UA_CPU, createUAParserItem(UA_CPU))
.set(UA_DEVICE, createUAParserItem(UA_DEVICE))
@@ -1084,7 +1032,6 @@ const window = undefined;
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
};
this.set('ua', ua)
- .set('ua_ch', uaCH)
.set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
.set(UA_CPU, createUAParserItemWithCH(UA_CPU))
.set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
diff --git a/test/cpu-test.json b/test/cpu-test.json
index cf314f9c8..2a7d18920 100644
--- a/test/cpu-test.json
+++ b/test/cpu-test.json
@@ -206,5 +206,13 @@
{
"architecture" : "irix64"
}
+ },
+ {
+ "desc" : "68k",
+ "ua" : "'Mozilla/1.1 (Macintosh; U; 68K)'",
+ "expect" :
+ {
+ "architecture" : "68k"
+ }
}
]
diff --git a/test/es6-test.mjs b/test/test-es6.mjs
similarity index 84%
rename from test/es6-test.mjs
rename to test/test-es6.mjs
index e3e0cbc24..8165ec996 100644
--- a/test/es6-test.mjs
+++ b/test/test-es6.mjs
@@ -7,7 +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 },
+ //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 },
cpu: { architecture: undefined },
device: { vendor: undefined, model: undefined, type: undefined },
From b09878934fce1de9bcadace72cc3a6fd6c613e34 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 29 Mar 2023 22:52:56 +0700
Subject: [PATCH 061/388] Prevent altering the result when supplied user-agent
is different from current user-agent
---
dist/ua-parser.min.js | 2 +-
dist/ua-parser.pack.js | 2 +-
readme.md | 2 +-
src/ua-parser.js | 9 +++++----
src/ua-parser.mjs | 9 +++++----
5 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index 2936c2e63..9bba36131 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.1
Copyright © 2012-2023 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.1",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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=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},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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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]&&NAVIGATOR_UADATA[PLATFORM]!="Unknown"){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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-alpha.1",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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=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},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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 c5d1e9c43..ca1bc8f58 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.1
Copyright © 2012-2023 Faisal Salman
MIT License */
-!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,n,a,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
+!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,a,n,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
diff --git a/readme.md b/readme.md
index ad229f322..fe1ec957b 100644
--- a/readme.md
+++ b/readme.md
@@ -477,7 +477,7 @@ http.createServer(function (req, res) {
res.setHeader('Accept-CH', getHighEntropyValues);
res.setHeader('Critical-CH', getHighEntropyValues);
- var ua = uap(req.headers);
+ var ua = uap(req.headers).withClientHints();
// END since@2.0 */
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 6a82ed1ad..51cb0fa3c 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -932,26 +932,27 @@
['data', createUAParserData(itemType, ua, rgxMap, uaCH)]
]);
this.parse();
+ var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua;
switch(this.itemType) {
case UA_BROWSER:
// Brave-specific detection
- if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
this.set(NAME, 'Brave');
}
this.set(MAJOR, majorize(this.get(VERSION)));
break;
case UA_DEVICE:
- if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad')
.set(TYPE, TABLET);
}
break;
case UA_OS:
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
+ if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
break;
diff --git a/src/ua-parser.mjs b/src/ua-parser.mjs
index e0d93d0d8..4d5df1b29 100644
--- a/src/ua-parser.mjs
+++ b/src/ua-parser.mjs
@@ -936,26 +936,27 @@ const window = undefined;
['data', createUAParserData(itemType, ua, rgxMap, uaCH)]
]);
this.parse();
+ var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua;
switch(this.itemType) {
case UA_BROWSER:
// Brave-specific detection
- if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
this.set(NAME, 'Brave');
}
this.set(MAJOR, majorize(this.get(VERSION)));
break;
case UA_DEVICE:
- if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad')
.set(TYPE, TABLET);
}
break;
case UA_OS:
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') {
+ if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
break;
From 1e80cf3533559796e115d15875a0613fe619eb47 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 29 Mar 2023 23:42:30 +0700
Subject: [PATCH 062/388] Only set browser as Chromium if no other brand was
found
---
src/ua-parser.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 51cb0fa3c..76d58192c 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -991,8 +991,8 @@
if (brands) {
for (var i in brands) {
var brandName = brands[i].brand,
- brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
+ brandVersion = brands[i].version
+ if (!/not.a.brand/i.test(brandName) && i < 1 || /chromi/i.test(this.get(NAME))) {
this.set(NAME, strip(GOOGLE+' ', brandName))
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
From 359cbecd32ec47f2f1e566310c34ef02d85d3dc2 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 2 Apr 2023 05:53:45 +0700
Subject: [PATCH 063/388] Fix #640 - Self-return thenable causing infinite-loop
when awaited
---
readme.md | 24 ++++++++++++++++++++----
src/ua-parser.js | 21 +++++++++++++++++----
2 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/readme.md b/readme.md
index fe1ec957b..999898593 100644
--- a/readme.md
+++ b/readme.md
@@ -287,9 +287,9 @@ engine.version; // "28.0.1500.95"
engine.toString(); // "Blink 28.0.1500.95"
```
-#### * `withClientHints():Promise|Thenable` `since@2.0`
+#### * `withClientHints():Promise|Thenable|object` `since@2.0`
-Recently Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`.
+Recently, Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`.
```js
// client-side example
@@ -311,7 +311,7 @@ Recently Chrome limits the information exposed through user-agent and introduces
})();
```
-Along with `User-Agent` HTTP header, Chrome also sends this client-hints data by default under `Sec-CH-UA-*` HTTP headers in each request. In server-side development, you can capture this extra information by passing the `req.headers` to `UAParser()` (see examples below). When using `withClientHints()` in nodejs environment and browser without client-hints support (basically anything that's not Chromium-based) the updated data will be returned as a `Thenable` (has `then()` method).
+Along with `User-Agent` HTTP header, Chrome also sends this client-hints data by default under `Sec-CH-UA-*` HTTP headers in each request. In server-side development, you can capture this extra information by passing the `req.headers` to `UAParser()` (see examples below). When using `withClientHints()` in nodejs environment and browser without client-hints support (basically anything that's not Chromium-based), it will returns a new object with updated data.
```js
// server-side example
@@ -338,7 +338,10 @@ console.log(result2.os.name); // "Android"
console.log(result2.device.type); // "mobile"
console.log(result2.device.model); // "Galaxy S3 Marketing"
-new UAParser(request.headers).getBrowser().withClientHints().then((browser) => {
+new UAParser(request.headers)
+ .getBrowser()
+ .withClientHints()
+ .then((browser) => {
console.log(browser.toString()); // Chrome 110.0.0.0
});
```
@@ -378,6 +381,19 @@ let myParser2 = new UAParser({
console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}
```
+Some basic extensions (although not very complete at the moment) can also be found under `ua-parser-js/extensions` submodule.
+
+```js
+import { UAParser } from 'ua-parser-js';
+import { Emails } from 'ua-parser-js/extensions';
+
+const browser = new UAParser(Emails)
+ .setUA('Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0')
+ .getBrowser();
+
+console.log(browser.name); // Thunderbird
+```
+
# Usage
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 76d58192c..b62138232 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -888,8 +888,21 @@
if (!NAVIGATOR_UADATA) {
UAParserData.prototype.then = function (cb) {
- cb(this);
- return this;
+ var that = this;
+ var UAParserDataResolve = function () {
+ for (var prop in that) {
+ if (that.hasOwnProperty(prop)) {
+ this[prop] = that[prop];
+ }
+ }
+ };
+ UAParserDataResolve.prototype = {
+ is : UAParserData.prototype.is,
+ toString : UAParserData.prototype.toString
+ };
+ var resolveData = new UAParserDataResolve();
+ cb(resolveData);
+ return resolveData;
};
}
@@ -991,8 +1004,8 @@
if (brands) {
for (var i in brands) {
var brandName = brands[i].brand,
- brandVersion = brands[i].version
- if (!/not.a.brand/i.test(brandName) && i < 1 || /chromi/i.test(this.get(NAME))) {
+ brandVersion = brands[i].version;
+ if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
this.set(NAME, strip(GOOGLE+' ', brandName))
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
From 5d2acd8fe7e8029f09afebf9c8afb5bcf4bcd951 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 2 Apr 2023 17:55:18 +0700
Subject: [PATCH 064/388] Bump version 2.0.0-alpha.2
---
bower.json | 2 +-
changelog.md | 42 +++++++++++++++++----------
dist/ua-parser.min.js | 4 +--
dist/ua-parser.pack.js | 4 +--
package.js | 2 +-
package.json | 2 +-
src/enum/ua-parser-enum.js | 2 +-
src/enum/ua-parser-enum.mjs | 2 +-
src/extension/ua-parser-extension.js | 2 +-
src/extension/ua-parser-extension.mjs | 2 +-
src/ua-parser.js | 8 ++---
src/ua-parser.mjs | 27 ++++++++++++-----
12 files changed, 62 insertions(+), 37 deletions(-)
diff --git a/bower.json b/bower.json
index 05b8579f8..0f5d520fd 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "ua-parser-js",
- "version": "2.0.0-alpha.1",
+ "version": "2.0.0-alpha.2",
"authors": [
"Faisal Salman "
],
diff --git a/changelog.md b/changelog.md
index a4aa6f1e3..76d4ef8ec 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,16 +1,32 @@
# UAParser.js Changelog
-# Version 2.0.0-alpha.1
-- Breaking changes:
- - Browser detection on mobile device: `Chrome => Mobile Chrome`, `Firefox => Mobile Firefox`
- - OS detection: `Mac OS => macOS`, `Chromium OS => Chrome OS`
-- Add some new methods in result object:
- - Add support for client hints: `withClientHints()`
- - Utility for easy comparison: `is()`
- - Utility to print full-name: `toString()`
-- Add support for ES module `import { UAParser } from 'ua-parser-js'`
-- Provide Enums `'ua-parser-js/enums'`
-- Provide Extensions `'ua-parser-js/extensions'`
+# Version 2.0
+- What's breaking:
+ - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"`
+ - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"`
+- What's new:
+ - Add some new methods in result object:
+ - Add support for client hints: `withClientHints()`
+ - Utility for easy comparison: `is()`
+ - Utility to print full-name: `toString()`
+ - Add support for ES module `import { UAParser } from 'ua-parser-js'`
+ - Provide Enums `'ua-parser-js/enums'`
+ - Provide Extensions `'ua-parser-js/extensions'`
+
+## Version 2.0.0-alpha.2
+- Fix browser result always returning Chromium when using `withClientHints()`
+- Fix infinite-loop when await-ing `withClientHints()` in non-client-hints browser
+
+## Version 2.0.0-alpha.1
+- Initial work on new major version
+
+
+# Version 0.7 / 1.0
+
+Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion.
+
+## Version 0.7.35 / 1.0.35
+- Fix result from user-supplied user-agent being altered
- Add new browser: Heytap, TikTok
- Add new engine: LibWeb
- Add new OS: SerenityOS
@@ -18,10 +34,6 @@
- Improve device detection: iPhone, Amazon Echo
- Improve OS detection: iOS
-# Version 0.7 / 1.0
-
-Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion.
-
## Version 0.7.34 / 1.0.34
- Fix Sharp Mobile detected as Huawei Tablet
- Fix IE8 bug
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index 9bba36131..f928f0542 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
-/* UAParser.js v2.0.0-alpha.1
+/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.1",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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=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},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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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-alpha.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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=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},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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 ca1bc8f58..50850b5f9 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
-/* UAParser.js v2.0.0-alpha.1
+/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License */
-!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,a,n,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
+!function(i,u){"use strict";function e(i){for(var e={},t=0;tT?yi(i,T):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=W&&l.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,s=i[r].version;!/not.a.brand/i.test(a)&&(r<1||/chromi/i.test(this.get(g)))&&this.set(g,xi(K+" ",a)).set(x,s).set(f,vi(s))}break;case B:var n=t[k];n&&(n&&"64"==t[A]&&(n+="64"),l.call(this.data,n+";",o));break;case $:t[y]&&this.set(v,y),t[m]&&this.set(m,t[m]);break;case L:n=t[N];n&&(w=t[z],n==ui&&(w=13<=parseInt(vi(w),10)?"11":"10"),this.set(g,n).set(x,w));break;case W:var w=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,w(V)).set(B,w(B)).set($,w($)).set(D,w(D)).set(L,w(L))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.2",zi.BROWSER=e([g,x,f]),zi.CPU=e([k]),zi.DEVICE=e([m,o,v,r,y,a,n,w,_]),zi.ENGINE=zi.OS=e([g,x]),typeof exports!==p?(typeof module!==p&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===d&&define.amd?define(function(){return zi}):typeof i!==p&&(i.UAParser=zi);var Ai,Oi=typeof i!==p&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
diff --git a/package.js b/package.js
index 3f2ddf1cb..cb7f57ac0 100644
--- a/package.js
+++ b/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'faisalman:ua-parser-js',
- version: '2.0.0-alpha.1',
+ version: '2.0.0-alpha.2',
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 37191dcb7..8197b3fca 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"title": "UAParser.js",
"name": "ua-parser-js",
- "version": "2.0.0-alpha.1",
+ "version": "2.0.0-alpha.2",
"author": "Faisal Salman (http://faisalman.com)",
"description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment",
"keywords": [
diff --git a/src/enum/ua-parser-enum.js b/src/enum/ua-parser-enum.js
index 8333eaf8f..9276b9ace 100644
--- a/src/enum/ua-parser-enum.js
+++ b/src/enum/ua-parser-enum.js
@@ -1,5 +1,5 @@
///////////////////////////////////////////////
-/* Enums for UAParser.js v2.0.0-alpha.1
+/* Enums for UAParser.js v2.0.0-alpha.2
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/enum/ua-parser-enum.mjs b/src/enum/ua-parser-enum.mjs
index 7708532fa..07b791a94 100644
--- a/src/enum/ua-parser-enum.mjs
+++ b/src/enum/ua-parser-enum.mjs
@@ -3,7 +3,7 @@
// Source: /src/enum/ua-parser-enum.js
///////////////////////////////////////////////
-/* Enums for UAParser.js v2.0.0-alpha.1
+/* Enums for UAParser.js v2.0.0-alpha.2
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/extension/ua-parser-extension.js b/src/extension/ua-parser-extension.js
index dfa90ed50..8b1036636 100644
--- a/src/extension/ua-parser-extension.js
+++ b/src/extension/ua-parser-extension.js
@@ -1,5 +1,5 @@
///////////////////////////////////////////////
-/* Extensions for UAParser.js v2.0.0-alpha.1
+/* Extensions for UAParser.js v2.0.0-alpha.2
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/extension/ua-parser-extension.mjs b/src/extension/ua-parser-extension.mjs
index 8b645fc74..7a2f42a87 100644
--- a/src/extension/ua-parser-extension.mjs
+++ b/src/extension/ua-parser-extension.mjs
@@ -3,7 +3,7 @@
// Source: /src/extension/ua-parser-extension.js
///////////////////////////////////////////////
-/* Extensions for UAParser.js v2.0.0-alpha.1
+/* Extensions for UAParser.js v2.0.0-alpha.2
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/ua-parser.js b/src/ua-parser.js
index b62138232..3799b0245 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////////////
-/* UAParser.js v2.0.0-alpha.1
+/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -17,7 +17,7 @@
/////////////
- var LIBVERSION = '2.0.0-alpha.1',
+ var LIBVERSION = '2.0.0-alpha.2',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -332,7 +332,7 @@
/miuibrowser\/([\w\.]+)/i // MIUI Browser
], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [
/fxios\/([\w\.-]+)/i // Firefox for iOS
- ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [
+ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [
/\bqihu|(qi?ho?o?|360)browser/i // 360
], [[NAME, '360' + SUFFIX_BROWSER]], [
/(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i
@@ -391,7 +391,7 @@
// Gecko based
/(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile
- ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [
+ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [
/(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
], [[NAME, 'Netscape'], VERSION], [
/mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
diff --git a/src/ua-parser.mjs b/src/ua-parser.mjs
index 4d5df1b29..9b7dfc58a 100644
--- a/src/ua-parser.mjs
+++ b/src/ua-parser.mjs
@@ -5,7 +5,7 @@
const window = undefined;
/////////////////////////////////////////////////////////////////////////////////
-/* UAParser.js v2.0.0-alpha.1
+/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -21,7 +21,7 @@ const window = undefined;
/////////////
- var LIBVERSION = '2.0.0-alpha.1',
+ var LIBVERSION = '2.0.0-alpha.2',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -336,7 +336,7 @@ const window = undefined;
/miuibrowser\/([\w\.]+)/i // MIUI Browser
], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [
/fxios\/([\w\.-]+)/i // Firefox for iOS
- ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [
+ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [
/\bqihu|(qi?ho?o?|360)browser/i // 360
], [[NAME, '360' + SUFFIX_BROWSER]], [
/(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i
@@ -395,7 +395,7 @@ const window = undefined;
// Gecko based
/(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile
- ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [
+ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [
/(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
], [[NAME, 'Netscape'], VERSION], [
/mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
@@ -892,8 +892,21 @@ const window = undefined;
if (!NAVIGATOR_UADATA) {
UAParserData.prototype.then = function (cb) {
- cb(this);
- return this;
+ var that = this;
+ var UAParserDataResolve = function () {
+ for (var prop in that) {
+ if (that.hasOwnProperty(prop)) {
+ this[prop] = that[prop];
+ }
+ }
+ };
+ UAParserDataResolve.prototype = {
+ is : UAParserData.prototype.is,
+ toString : UAParserData.prototype.toString
+ };
+ var resolveData = new UAParserDataResolve();
+ cb(resolveData);
+ return resolveData;
};
}
@@ -996,7 +1009,7 @@ const window = undefined;
for (var i in brands) {
var brandName = brands[i].brand,
brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) {
+ if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
this.set(NAME, strip(GOOGLE+' ', brandName))
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
From 4711805a1c89c3705fd910b9d68af6b4ef561212 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 5 Apr 2023 22:28:35 +0700
Subject: [PATCH 065/388] Re-use previous result instead of re-parse it all
over again
---
src/ua-parser.js | 64 +++++++++++++++++++++++++++---------------------
1 file changed, 36 insertions(+), 28 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 3799b0245..19b478085 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -846,10 +846,14 @@
assignFromEntries.call(this, init_props);
}
UAParserData.prototype.withClientHints = function () {
+
+ var prevData = this;
// nodejs / non-client-hints browsers
if (!NAVIGATOR_UADATA) {
- return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
+ var item = new UAParserItem(itemType, ua, rgxMap, uaCH);
+ item.data = prevData;
+ return item.parseCH().get();
}
// browsers based on chromium 85+
@@ -857,7 +861,9 @@
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
var JS_UACH = new UAParserDataCH(res, false);
- return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get();
+ var item = new UAParserItem(itemType, ua, rgxMap, JS_UACH);
+ item.data = prevData;
+ return item.parseCH().get();
});
};
@@ -944,8 +950,29 @@
['rgxMap', rgxMap],
['data', createUAParserData(itemType, ua, rgxMap, uaCH)]
]);
- this.parse();
- var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua;
+ if(this.itemType == UA_RESULT) {
+ var createUAParserItem = function (itemType) {
+ return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseUA().get();
+ };
+ this.set('ua', ua)
+ .set(UA_BROWSER, createUAParserItem(UA_BROWSER))
+ .set(UA_CPU, createUAParserItem(UA_CPU))
+ .set(UA_DEVICE, createUAParserItem(UA_DEVICE))
+ .set(UA_ENGINE, createUAParserItem(UA_ENGINE))
+ .set(UA_OS, createUAParserItem(UA_OS))
+ .get();
+ }
+ return this;
+ }
+ UAParserItem.prototype.get = function (prop) {
+ if (!prop) return this.data;
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
+ };
+ UAParserItem.prototype.parseUA = function () {
+ if (this.itemType != UA_RESULT) {
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
+ }
+ var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == this.ua;
switch(this.itemType) {
case UA_BROWSER:
// Brave-specific detection
@@ -968,28 +995,6 @@
if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
- break;
- case UA_RESULT:
- var createUAParserItem = function (itemType) {
- return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
- };
- this.set('ua', ua)
- .set(UA_BROWSER, createUAParserItem(UA_BROWSER))
- .set(UA_CPU, createUAParserItem(UA_CPU))
- .set(UA_DEVICE, createUAParserItem(UA_DEVICE))
- .set(UA_ENGINE, createUAParserItem(UA_ENGINE))
- .set(UA_OS, createUAParserItem(UA_OS))
- .get();
- }
- return this;
- }
- UAParserItem.prototype.get = function (prop) {
- if (!prop) return this.data;
- return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
- };
- UAParserItem.prototype.parse = function () {
- if (this.itemType != UA_RESULT) {
- rgxMapper.call(this.data, this.ua, this.rgxMap);
}
return this;
};
@@ -1038,8 +1043,11 @@
}
break;
case UA_RESULT:
+ var prevData = this.data;
var createUAParserItemWithCH = function (itemType) {
- return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
+ var item = new UAParserItem(itemType, ua, rgxMap[itemType], uaCH);
+ item.data = prevData[itemType];
+ return item.parseCH().get();
};
this.set('ua', ua)
.set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
@@ -1092,7 +1100,7 @@
createUAParserItemFunc = function (itemType) {
return function () {
- return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get();
+ return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).parseUA().get();
};
};
From b385a73340f84c43b78d41e8147368c1fdba158b Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 5 Apr 2023 23:29:27 +0700
Subject: [PATCH 066/388] Move feature detection to its own dedicated method
---
src/ua-parser.js | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 19b478085..702bf7e9f 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -964,14 +964,7 @@
}
return this;
}
- UAParserItem.prototype.get = function (prop) {
- if (!prop) return this.data;
- return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
- };
- UAParserItem.prototype.parseUA = function () {
- if (this.itemType != UA_RESULT) {
- rgxMapper.call(this.data, this.ua, this.rgxMap);
- }
+ UAParserItem.prototype.detectFeature = function () {
var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == this.ua;
switch(this.itemType) {
case UA_BROWSER:
@@ -998,6 +991,17 @@
}
return this;
};
+ UAParserItem.prototype.get = function (prop) {
+ if (!prop) return this.data;
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
+ };
+ UAParserItem.prototype.parseUA = function () {
+ if (this.itemType != UA_RESULT) {
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
+ }
+ this.detectFeature();
+ return this;
+ };
UAParserItem.prototype.parseCH = function () {
var ua = this.ua,
uaCH = this.uaCH,
From 59d8d836c2f326a753813b8a39ad079c55c6663c Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 6 Apr 2023 05:48:14 +0700
Subject: [PATCH 067/388] Clean up: remove travis, verup; move jshint config to
inline
---
.jshintrc | 3 ---
.travis.yml | 18 ------------------
package.json | 19 ++++---------------
src/ua-parser.js | 4 +++-
4 files changed, 7 insertions(+), 37 deletions(-)
delete mode 100644 .jshintrc
delete mode 100644 .travis.yml
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index 5798043ca..000000000
--- a/.jshintrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "esversion": 3
-}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 0f87fc37f..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-arch:
- - amd64
- - ppc64le
-language: node_js
-node_js:
- - stable
- - lts/*
-
-notifications:
- email: false
-
-cache:
- directories:
- - node_modules
-
-sudo: false
-
-script: npm run test-ci
\ No newline at end of file
diff --git a/package.json b/package.json
index 8197b3fca..cdd0bb2aa 100644
--- a/package.json
+++ b/package.json
@@ -3,9 +3,10 @@
"name": "ua-parser-js",
"version": "2.0.0-alpha.2",
"author": "Faisal Salman (http://faisalman.com)",
- "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment",
+ "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment",
"keywords": [
"user-agent",
+ "client-hints",
"parser",
"browser",
"engine",
@@ -162,23 +163,12 @@
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
+ "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\/\\*jshint[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
"test": "jshint src/ua-parser.js && mocha -R nyan test",
"test-ci": "jshint src/ua-parser.js && mocha -R spec test",
"verup": "node ./node_modules/verup",
"version": "node ./node_modules/verup 0"
},
- "verup": {
- "files": [
- "bower.json",
- "package.js",
- "src/ua-parser.js"
- ],
- "regs": [
- "^((?:\\$|(\\s*\\*\\s*@)|(\\s*(?:var|,)?\\s+))(?:LIBVERSION|version)[\\s\\:='\"]+)([0-9]+(?:\\.[0-9]+){2,2})",
- "^(\\/?\\s?\\*.*v)([0-9]+(?:\\.[0-9]+){2,2})"
- ]
- },
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
@@ -186,8 +176,7 @@
"mocha": "~8.2.0",
"requirejs": "^2.3.2",
"safe-regex": "^2.1.1",
- "uglify-js": "~3.12.0",
- "verup": "^1.3.x"
+ "uglify-js": "~3.12.0"
},
"repository": {
"type": "git",
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 702bf7e9f..8e6c89180 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -8,10 +8,12 @@
Source : https://github.com/faisalman/ua-parser-js */
/////////////////////////////////////////////////////////////////////////////////
+/*jshint esversion: 3 */
+
(function (window, undefined) {
'use strict';
-
+
//////////////
// Constants
/////////////
From e01663b48fa68680a795606dbbff1e2141ec68ab Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 8 Apr 2023 04:40:59 +0700
Subject: [PATCH 068/388] Rearrange internal class & remove old Safari map
---
src/ua-parser.js | 359 +++++++++++++++++++++--------------------
test/browser-test.json | 4 +-
2 files changed, 189 insertions(+), 174 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 8e6c89180..ffa73eaa0 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -95,18 +95,7 @@
// Helper
//////////
- var assignFromEntries = 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;
- },
- extend = function (regexes, extensions) {
+ 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];
@@ -150,6 +139,17 @@
majorize = function (version) {
return typeof(version) === STR_TYPE ? 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 str.replace(pattern, EMPTY);
},
@@ -243,18 +243,7 @@
// String map
//////////////
- // Safari < 3.0
- var oldSafariMap = {
- '1.0' : '/8',
- '1.2' : '/1',
- '1.3' : '/3',
- '2.0' : '/412',
- '2.0.2' : '/416',
- '2.0.3' : '/417',
- '2.0.4' : '/419',
- '?' : '/'
- },
- windowsVersionMap = {
+ var windowsVersionMap = {
'ME' : '4.90',
'NT 3.11' : 'NT3.51',
'NT 4.0' : 'NT4.0',
@@ -386,7 +375,7 @@
/version\/([\w\.\,]+) .*(safari)/i // Safari
], [VERSION, NAME], [
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
- ], [NAME, [VERSION, strMapper, oldSafariMap]], [
+ ], [NAME, [VERSION, '1']], [
/(webkit|khtml)\/([\w\.]+)/i
], [NAME, VERSION], [
@@ -427,7 +416,7 @@
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
], [[ARCHITECTURE, 'ia32']], [
- /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
+ /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
], [[ARCHITECTURE, 'arm64']], [
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
@@ -811,23 +800,23 @@
var defaultProps = (function () {
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
- assignFromEntries.call(props.init, [
+ setProps.call(props.init, [
[UA_BROWSER, [NAME, VERSION, MAJOR]],
[UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [TYPE, MODEL, VENDOR]],
[UA_ENGINE, [NAME, VERSION]],
[UA_OS, [NAME, VERSION]]
]);
- assignFromEntries.call(props.isIgnore, [
+ setProps.call(props.isIgnore, [
[UA_BROWSER, [VERSION, MAJOR]],
[UA_ENGINE, [VERSION]],
[UA_OS, [VERSION]]
]);
- assignFromEntries.call(props.isIgnoreRgx, [
+ setProps.call(props.isIgnoreRgx, [
[UA_BROWSER, / ?browser$/i],
[UA_OS, / ?os$/i]
]);
- assignFromEntries.call(props.toString, [
+ setProps.call(props.toString, [
[UA_BROWSER, [NAME, VERSION]],
[UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [VENDOR, MODEL]],
@@ -837,7 +826,7 @@
return props;
})();
- var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
+ var createUAParserData = function (item, itemType) {
var init_props = defaultProps.init[itemType],
is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
@@ -845,27 +834,30 @@
toString_props = defaultProps.toString[itemType] || 0;
function UAParserData () {
- assignFromEntries.call(this, init_props);
+ setProps.call(this, init_props);
}
+
+ UAParserData.prototype.getItem = function () {
+ return item;
+ };
+
UAParserData.prototype.withClientHints = function () {
- var prevData = this;
-
// nodejs / non-client-hints browsers
if (!NAVIGATOR_UADATA) {
- var item = new UAParserItem(itemType, ua, rgxMap, uaCH);
- item.data = prevData;
- return item.parseCH().get();
+ return item
+ .parseCH()
+ .get();
}
// browsers based on chromium 85+
return NAVIGATOR_UADATA
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
- var JS_UACH = new UAParserDataCH(res, false);
- var item = new UAParserItem(itemType, ua, rgxMap, JS_UACH);
- item.data = prevData;
- return item.parseCH().get();
+ return item
+ .setCH(new UAParserDataCH(res, false))
+ .parseCH()
+ .get();
});
};
@@ -923,9 +915,9 @@
function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
- assignFromEntries.call(this, CH_ALL_VALUES);
+ setProps.call(this, CH_ALL_VALUES);
if (isHTTP_UACH) {
- assignFromEntries.call(this, [
+ setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
[BRANDS, itemListToArray(uach[CH_HEADER])],
@@ -941,133 +933,138 @@
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
}
}
- return this;
}
function UAParserItem (itemType, ua, rgxMap, uaCH) {
- assignFromEntries.call(this, [
+
+ this.get = function (prop) {
+ if (!prop) return this.data;
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
+ };
+
+ this.set = function (prop, val) {
+ this.data[prop] = val;
+ return this;
+ };
+
+ this.setCH = function (ch) {
+ this.uaCH = ch;
+ return this;
+ };
+
+ this.detectFeature = function () {
+ if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
+ switch (this.itemType) {
+ case UA_BROWSER:
+ // Brave-specific detection
+ if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ this.set(NAME, 'Brave');
+ }
+ break;
+ case UA_DEVICE:
+ // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ this.set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
+ }
+ break;
+ case UA_OS:
+ // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
+ }
+ }
+ }
+ 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 ua = this.ua,
+ uaCH = this.uaCH,
+ rgxMap = this.rgxMap;
+
+ switch (this.itemType) {
+ case UA_BROWSER:
+ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
+ if (brands) {
+ for (var i in brands) {
+ var brandName = brands[i].brand,
+ brandVersion = brands[i].version;
+ if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
+ this.set(NAME, strip(GOOGLE+' ', brandName))
+ .set(VERSION, brandVersion)
+ .set(MAJOR, majorize(brandVersion));
+ }
+ }
+ }
+ 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]);
+ }
+ break;
+ case UA_OS:
+ var osName = uaCH[PLATFORM];
+ if(osName) {
+ var osVersion = uaCH[PLATFORMVER];
+ if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
+ }
+ break;
+ case UA_RESULT:
+ var data = this.data;
+ var parse = function (itemType) {
+ return data[itemType]
+ .getItem()
+ .setCH(uaCH)
+ .parseCH()
+ .get();
+ };
+ this.set('ua', ua)
+ .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', createUAParserData(itemType, ua, rgxMap, uaCH)]
+ ['data', createUAParserData(this, itemType)]
]);
- if(this.itemType == UA_RESULT) {
- var createUAParserItem = function (itemType) {
- return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseUA().get();
- };
- this.set('ua', ua)
- .set(UA_BROWSER, createUAParserItem(UA_BROWSER))
- .set(UA_CPU, createUAParserItem(UA_CPU))
- .set(UA_DEVICE, createUAParserItem(UA_DEVICE))
- .set(UA_ENGINE, createUAParserItem(UA_ENGINE))
- .set(UA_OS, createUAParserItem(UA_OS))
- .get();
- }
+
return this;
}
- UAParserItem.prototype.detectFeature = function () {
- var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == this.ua;
- switch(this.itemType) {
- case UA_BROWSER:
- // Brave-specific detection
- if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
- this.set(NAME, 'Brave');
- }
- this.set(MAJOR, majorize(this.get(VERSION)));
- break;
- case UA_DEVICE:
- if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
- this.set(MODEL, 'iPad')
- .set(TYPE, TABLET);
- }
- break;
- case UA_OS:
- if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
- }
- }
- return this;
- };
- UAParserItem.prototype.get = function (prop) {
- if (!prop) return this.data;
- return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
- };
- UAParserItem.prototype.parseUA = function () {
- if (this.itemType != UA_RESULT) {
- rgxMapper.call(this.data, this.ua, this.rgxMap);
- }
- this.detectFeature();
- return this;
- };
- UAParserItem.prototype.parseCH = function () {
- var ua = this.ua,
- uaCH = this.uaCH,
- rgxMap = this.rgxMap;
-
- switch (this.itemType) {
- case UA_BROWSER:
- var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
- if (brands) {
- for (var i in brands) {
- var brandName = brands[i].brand,
- brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
- this.set(NAME, strip(GOOGLE+' ', brandName))
- .set(VERSION, brandVersion)
- .set(MAJOR, majorize(brandVersion));
- }
- }
- }
- 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]);
- }
- break;
- case UA_OS:
- var osName = uaCH[PLATFORM];
- if(osName) {
- var osVersion = uaCH[PLATFORMVER];
- if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
- this.set(NAME, osName)
- .set(VERSION, osVersion);
- }
- break;
- case UA_RESULT:
- var prevData = this.data;
- var createUAParserItemWithCH = function (itemType) {
- var item = new UAParserItem(itemType, ua, rgxMap[itemType], uaCH);
- item.data = prevData[itemType];
- return item.parseCH().get();
- };
- this.set('ua', ua)
- .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
- .set(UA_CPU, createUAParserItemWithCH(UA_CPU))
- .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
- .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
- .set(UA_OS, createUAParserItemWithCH(UA_OS));
- }
- return this;
- };
- UAParserItem.prototype.set = function (prop, val) {
- this.data[prop] = val;
- return this;
- };
function UAParser (ua, extensions, headers) {
@@ -1104,26 +1101,44 @@
extend(defaultRegexes, extensions) :
defaultRegexes,
- createUAParserItemFunc = function (itemType) {
- return function () {
- return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).parseUA().get();
- };
+ createItemFunc = function (itemType) {
+ if (itemType == UA_RESULT) {
+ return function () {
+ return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH)
+ .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 UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
+ .parseUA()
+ .detectFeature()
+ .get();
+ };
+ }
};
// public methods
- assignFromEntries.call(this, [
- ['getBrowser', createUAParserItemFunc(UA_BROWSER)],
- ['getCPU', createUAParserItemFunc(UA_CPU)],
- ['getDevice', createUAParserItemFunc(UA_DEVICE)],
- ['getEngine', createUAParserItemFunc(UA_ENGINE)],
- ['getOS', createUAParserItemFunc(UA_OS)],
- ['getResult', createUAParserItemFunc(UA_RESULT)],
+ 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) {
userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
return this;
}]
- ]).setUA(userAgent);
+ ])
+ .setUA(userAgent);
+
return this;
}
diff --git a/test/browser-test.json b/test/browser-test.json
index b8f7a6e80..9ca190c61 100644
--- a/test/browser-test.json
+++ b/test/browser-test.json
@@ -1084,8 +1084,8 @@
"expect" :
{
"name" : "Safari",
- "version" : "2.0.4",
- "major" : "2"
+ "version" : "1",
+ "major" : "1"
}
},
{
From 625ece73e2d1adb3dca1c85defbc1a5069dd68de Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 8 Apr 2023 06:44:30 +0700
Subject: [PATCH 069/388] Rearrange test files & config
---
dist/ua-parser.min.js | 2 +-
dist/ua-parser.pack.js | 2 +-
package.json | 20 +-
script/build-module.js | 39 ++
src/{enum => module}/ua-parser-enum.mjs | 6 +-
.../ua-parser-extension.mjs | 6 +-
src/{ => module}/ua-parser.mjs | 354 ++++++++++--------
src/{enum => }/ua-parser-enum.js | 4 +-
src/{extension => }/ua-parser-extension.js | 4 +-
src/ua-parser.js | 3 +-
.../browser-all.json} | 0
test/{cpu-test.json => specs/cpu-all.json} | 0
.../device-all.json} | 0
.../engine-all.json} | 0
.../extension-device.json} | 0
.../extension-mediaplayer.json} | 0
test/{os-test.json => specs/os-all.json} | 0
test/test.js | 127 +------
18 files changed, 262 insertions(+), 305 deletions(-)
create mode 100644 script/build-module.js
rename src/{enum => module}/ua-parser-enum.mjs (97%)
rename src/{extension => module}/ua-parser-extension.mjs (98%)
rename src/{ => module}/ua-parser.mjs (86%)
rename src/{enum => }/ua-parser-enum.js (98%)
rename src/{extension => }/ua-parser-extension.js (99%)
rename test/{browser-test.json => specs/browser-all.json} (100%)
rename test/{cpu-test.json => specs/cpu-all.json} (100%)
rename test/{device-test.json => specs/device-all.json} (100%)
rename test/{engine-test.json => specs/engine-all.json} (100%)
rename test/{test-extension.json => specs/extension-device.json} (100%)
rename test/{mediaplayer-test.json => specs/extension-mediaplayer.json} (100%)
rename test/{os-test.json => specs/os-all.json} (100%)
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index f928f0542..d5b153cca 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=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},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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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-alpha.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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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])}}}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 ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set("ua",ua).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",createUAParserData(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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAParserItem(itemType,userAgent,regexMap,HTTP_UACH).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 UAParserItem(itemType,userAgent,regexMap[itemType],HTTP_UACH).parseUA().detectFeature().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){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 50850b5f9..141057c29 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License */
-!function(i,u){"use strict";function e(i){for(var e={},t=0;tT?yi(i,T):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=W&&l.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,s=i[r].version;!/not.a.brand/i.test(a)&&(r<1||/chromi/i.test(this.get(g)))&&this.set(g,xi(K+" ",a)).set(x,s).set(f,vi(s))}break;case B:var n=t[k];n&&(n&&"64"==t[A]&&(n+="64"),l.call(this.data,n+";",o));break;case $:t[y]&&this.set(v,y),t[m]&&this.set(m,t[m]);break;case L:n=t[N];n&&(w=t[z],n==ui&&(w=13<=parseInt(vi(w),10)?"11":"10"),this.set(g,n).set(x,w));break;case W:var w=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,w(V)).set(B,w(B)).set($,w($)).set(D,w(D)).set(L,w(L))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.2",zi.BROWSER=e([g,x,f]),zi.CPU=e([k]),zi.DEVICE=e([m,o,v,r,y,a,n,w,_]),zi.ENGINE=zi.OS=e([g,x]),typeof exports!==p?(typeof module!==p&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===d&&define.amd?define(function(){return zi}):typeof i!==p&&(i.UAParser=zi);var Ai,Oi=typeof i!==p&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
+!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
diff --git a/package.json b/package.json
index cdd0bb2aa..259988cca 100644
--- a/package.json
+++ b/package.json
@@ -143,19 +143,19 @@
],
"type": "commonjs",
"main": "src/ua-parser.js",
- "module": "src/ua-parser.mjs",
+ "module": "src/module/ua-parser.mjs",
"exports": {
".": {
"require": "./src/ua-parser.js",
- "import": "./src/ua-parser.mjs"
+ "import": "./src/module/ua-parser.mjs"
},
"./enums": {
- "require": "./src/enum/ua-parser-enum.js",
- "import": "./src/enum/ua-parser-enum.mjs"
+ "require": "./src/ua-parser-enum.js",
+ "import": "./src/module/ua-parser-enum.mjs"
},
"./extensions": {
- "require": "./src/extension/ua-parser-extension.js",
- "import": "./src/extension/ua-parser-extension.mjs"
+ "require": "./src/ua-parser-extension.js",
+ "import": "./src/module/ua-parser-extension.mjs"
}
},
"files": [
@@ -163,11 +163,9 @@
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\/\\*jshint[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
- "test": "jshint src/ua-parser.js && mocha -R nyan test",
- "test-ci": "jshint src/ua-parser.js && mocha -R spec test",
- "verup": "node ./node_modules/verup",
- "version": "node ./node_modules/verup 0"
+ "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js",
+ "test": "jshint src/ && mocha -R nyan test",
+ "test-ci": "jshint src/ && mocha -R spec test"
},
"devDependencies": {
"@babel/parser": "7.15.8",
diff --git a/script/build-module.js b/script/build-module.js
new file mode 100644
index 000000000..e002e55c7
--- /dev/null
+++ b/script/build-module.js
@@ -0,0 +1,39 @@
+const fs = require('fs');
+
+/*///////////////
+// ua-parser.mjs
+//////////////*/
+
+fs.writeFileSync('src/module/ua-parser.mjs',
+`// Generated ESM version of UAParser.js
+// DO NOT EDIT THIS FILE!
+// Source: /src/ua-parser.js
+
+` + fs.readFileSync('src/ua-parser.js','utf-8').replace(/\(func[\s\S]+strict\';/ig,'')
+ .replace(/esversion\: 3/ig, 'esversion: 6')
+ .replace(/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'),'utf-8');
+
+/*/////////////////////
+// ua-parser-enum.mjs
+////////////////////*/
+
+fs.writeFileSync('src/module/ua-parser-enum.mjs',
+`// Generated ESM version of UAParser.js enums
+// DO NOT EDIT THIS FILE!
+// Source: /src/ua-parser-enum.js
+
+` + fs.readFileSync('src/ua-parser-enum.js','utf-8')
+ .replace(/module\.exports =/ig,'export'),'utf-8');
+
+/*//////////////////////////
+// ua-parser-extension.mjs
+/////////////////////////*/
+
+fs.writeFileSync('src/module/ua-parser-extension.mjs',
+`// Generated ESM version of UAParser.js extensions
+// DO NOT EDIT THIS FILE!
+// Source: /src/ua-parser-extension.js
+
+` + fs.readFileSync('src/ua-parser-extension.js','utf-8')
+ .replace(/const UA.+\)/ig,'import UAParser from \'ua-parser-js\'')
+ .replace(/module\.exports =/ig,'export'),'utf-8');
\ No newline at end of file
diff --git a/src/enum/ua-parser-enum.mjs b/src/module/ua-parser-enum.mjs
similarity index 97%
rename from src/enum/ua-parser-enum.mjs
rename to src/module/ua-parser-enum.mjs
index 07b791a94..2081f10f5 100644
--- a/src/enum/ua-parser-enum.mjs
+++ b/src/module/ua-parser-enum.mjs
@@ -1,6 +1,6 @@
// Generated ESM version of UAParser.js enums
// DO NOT EDIT THIS FILE!
-// Source: /src/enum/ua-parser-enum.js
+// Source: /src/ua-parser-enum.js
///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.0-alpha.2
@@ -9,6 +9,8 @@
MIT License */
//////////////////////////////////////////////
+/*jshint esversion: 6 */
+
const BrowserName = Object.freeze({
CHROME : 'Chrome',
EDGE : 'Edge',
@@ -102,4 +104,4 @@ export {
DeviceVendor,
EngineName,
OSName
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/extension/ua-parser-extension.mjs b/src/module/ua-parser-extension.mjs
similarity index 98%
rename from src/extension/ua-parser-extension.mjs
rename to src/module/ua-parser-extension.mjs
index 7a2f42a87..44932a2c9 100644
--- a/src/extension/ua-parser-extension.mjs
+++ b/src/module/ua-parser-extension.mjs
@@ -1,6 +1,6 @@
// Generated ESM version of UAParser.js extensions
// DO NOT EDIT THIS FILE!
-// Source: /src/extension/ua-parser-extension.js
+// Source: /src/ua-parser-extension.js
///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.0-alpha.2
@@ -9,6 +9,8 @@
MIT License */
//////////////////////////////////////////////
+/*jshint esversion: 6 */
+
const MODEL = 'model';
const NAME = 'name';
const TYPE = 'type';
@@ -121,4 +123,4 @@ export {
ExtraDevices,
Emails,
Tools
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/ua-parser.mjs b/src/module/ua-parser.mjs
similarity index 86%
rename from src/ua-parser.mjs
rename to src/module/ua-parser.mjs
index 9b7dfc58a..ba554e354 100644
--- a/src/ua-parser.mjs
+++ b/src/module/ua-parser.mjs
@@ -2,8 +2,6 @@
// DO NOT EDIT THIS FILE!
// Source: /src/ua-parser.js
-const window = undefined;
-
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
@@ -14,8 +12,11 @@ const window = undefined;
Source : https://github.com/faisalman/ua-parser-js */
/////////////////////////////////////////////////////////////////////////////////
+/* jshint esversion: 6 */
+/* globals window */
+
//////////////
// Constants
/////////////
@@ -97,18 +98,7 @@ const window = undefined;
// Helper
//////////
- var assignFromEntries = 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;
- },
- extend = function (regexes, extensions) {
+ 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];
@@ -152,6 +142,17 @@ const window = undefined;
majorize = function (version) {
return typeof(version) === STR_TYPE ? 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 str.replace(pattern, EMPTY);
},
@@ -245,18 +246,7 @@ const window = undefined;
// String map
//////////////
- // Safari < 3.0
- var oldSafariMap = {
- '1.0' : '/8',
- '1.2' : '/1',
- '1.3' : '/3',
- '2.0' : '/412',
- '2.0.2' : '/416',
- '2.0.3' : '/417',
- '2.0.4' : '/419',
- '?' : '/'
- },
- windowsVersionMap = {
+ var windowsVersionMap = {
'ME' : '4.90',
'NT 3.11' : 'NT3.51',
'NT 4.0' : 'NT4.0',
@@ -388,7 +378,7 @@ const window = undefined;
/version\/([\w\.\,]+) .*(safari)/i // Safari
], [VERSION, NAME], [
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
- ], [NAME, [VERSION, strMapper, oldSafariMap]], [
+ ], [NAME, [VERSION, '1']], [
/(webkit|khtml)\/([\w\.]+)/i
], [NAME, VERSION], [
@@ -429,7 +419,7 @@ const window = undefined;
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
], [[ARCHITECTURE, 'ia32']], [
- /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
+ /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
], [[ARCHITECTURE, 'arm64']], [
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
@@ -813,23 +803,23 @@ const window = undefined;
var defaultProps = (function () {
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
- assignFromEntries.call(props.init, [
+ setProps.call(props.init, [
[UA_BROWSER, [NAME, VERSION, MAJOR]],
[UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [TYPE, MODEL, VENDOR]],
[UA_ENGINE, [NAME, VERSION]],
[UA_OS, [NAME, VERSION]]
]);
- assignFromEntries.call(props.isIgnore, [
+ setProps.call(props.isIgnore, [
[UA_BROWSER, [VERSION, MAJOR]],
[UA_ENGINE, [VERSION]],
[UA_OS, [VERSION]]
]);
- assignFromEntries.call(props.isIgnoreRgx, [
+ setProps.call(props.isIgnoreRgx, [
[UA_BROWSER, / ?browser$/i],
[UA_OS, / ?os$/i]
]);
- assignFromEntries.call(props.toString, [
+ setProps.call(props.toString, [
[UA_BROWSER, [NAME, VERSION]],
[UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [VENDOR, MODEL]],
@@ -839,7 +829,7 @@ const window = undefined;
return props;
})();
- var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
+ var createUAParserData = function (item, itemType) {
var init_props = defaultProps.init[itemType],
is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
@@ -847,21 +837,30 @@ const window = undefined;
toString_props = defaultProps.toString[itemType] || 0;
function UAParserData () {
- assignFromEntries.call(this, init_props);
+ setProps.call(this, init_props);
}
+
+ UAParserData.prototype.getItem = function () {
+ return item;
+ };
+
UAParserData.prototype.withClientHints = function () {
-
+
// nodejs / non-client-hints browsers
if (!NAVIGATOR_UADATA) {
- return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
+ return item
+ .parseCH()
+ .get();
}
// browsers based on chromium 85+
return NAVIGATOR_UADATA
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
- var JS_UACH = new UAParserDataCH(res, false);
- return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get();
+ return item
+ .setCH(new UAParserDataCH(res, false))
+ .parseCH()
+ .get();
});
};
@@ -919,9 +918,9 @@ const window = undefined;
function UAParserDataCH (uach, isHTTP_UACH) {
uach = uach || {};
- assignFromEntries.call(this, CH_ALL_VALUES);
+ setProps.call(this, CH_ALL_VALUES);
if (isHTTP_UACH) {
- assignFromEntries.call(this, [
+ setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
[BRANDS, itemListToArray(uach[CH_HEADER])],
@@ -937,127 +936,138 @@ const window = undefined;
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
}
}
- return this;
}
function UAParserItem (itemType, ua, rgxMap, uaCH) {
- assignFromEntries.call(this, [
+
+ this.get = function (prop) {
+ if (!prop) return this.data;
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
+ };
+
+ this.set = function (prop, val) {
+ this.data[prop] = val;
+ return this;
+ };
+
+ this.setCH = function (ch) {
+ this.uaCH = ch;
+ return this;
+ };
+
+ this.detectFeature = function () {
+ if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
+ switch (this.itemType) {
+ case UA_BROWSER:
+ // Brave-specific detection
+ if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
+ this.set(NAME, 'Brave');
+ }
+ break;
+ case UA_DEVICE:
+ // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
+ this.set(TYPE, MOBILE);
+ }
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
+ if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
+ this.set(MODEL, 'iPad')
+ .set(TYPE, TABLET);
+ }
+ break;
+ case UA_OS:
+ // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
+ }
+ }
+ }
+ 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 ua = this.ua,
+ uaCH = this.uaCH,
+ rgxMap = this.rgxMap;
+
+ switch (this.itemType) {
+ case UA_BROWSER:
+ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
+ if (brands) {
+ for (var i in brands) {
+ var brandName = brands[i].brand,
+ brandVersion = brands[i].version;
+ if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
+ this.set(NAME, strip(GOOGLE+' ', brandName))
+ .set(VERSION, brandVersion)
+ .set(MAJOR, majorize(brandVersion));
+ }
+ }
+ }
+ 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]);
+ }
+ break;
+ case UA_OS:
+ var osName = uaCH[PLATFORM];
+ if(osName) {
+ var osVersion = uaCH[PLATFORMVER];
+ if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
+ this.set(NAME, osName)
+ .set(VERSION, osVersion);
+ }
+ break;
+ case UA_RESULT:
+ var data = this.data;
+ var parse = function (itemType) {
+ return data[itemType]
+ .getItem()
+ .setCH(uaCH)
+ .parseCH()
+ .get();
+ };
+ this.set('ua', ua)
+ .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', createUAParserData(itemType, ua, rgxMap, uaCH)]
+ ['data', createUAParserData(this, itemType)]
]);
- this.parse();
- var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua;
- switch(this.itemType) {
- case UA_BROWSER:
- // Brave-specific detection
- if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
- this.set(NAME, 'Brave');
- }
- this.set(MAJOR, majorize(this.get(VERSION)));
- break;
- case UA_DEVICE:
- if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
- if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
- this.set(MODEL, 'iPad')
- .set(TYPE, TABLET);
- }
- break;
- case UA_OS:
- if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
- }
- break;
- case UA_RESULT:
- var createUAParserItem = function (itemType) {
- return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
- };
- this.set('ua', ua)
- .set(UA_BROWSER, createUAParserItem(UA_BROWSER))
- .set(UA_CPU, createUAParserItem(UA_CPU))
- .set(UA_DEVICE, createUAParserItem(UA_DEVICE))
- .set(UA_ENGINE, createUAParserItem(UA_ENGINE))
- .set(UA_OS, createUAParserItem(UA_OS))
- .get();
- }
+
return this;
}
- UAParserItem.prototype.get = function (prop) {
- if (!prop) return this.data;
- return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
- };
- UAParserItem.prototype.parse = function () {
- if (this.itemType != UA_RESULT) {
- rgxMapper.call(this.data, this.ua, this.rgxMap);
- }
- return this;
- };
- UAParserItem.prototype.parseCH = function () {
- var ua = this.ua,
- uaCH = this.uaCH,
- rgxMap = this.rgxMap;
-
- switch (this.itemType) {
- case UA_BROWSER:
- var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
- if (brands) {
- for (var i in brands) {
- var brandName = brands[i].brand,
- brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
- this.set(NAME, strip(GOOGLE+' ', brandName))
- .set(VERSION, brandVersion)
- .set(MAJOR, majorize(brandVersion));
- }
- }
- }
- 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]);
- }
- break;
- case UA_OS:
- var osName = uaCH[PLATFORM];
- if(osName) {
- var osVersion = uaCH[PLATFORMVER];
- if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
- this.set(NAME, osName)
- .set(VERSION, osVersion);
- }
- break;
- case UA_RESULT:
- var createUAParserItemWithCH = function (itemType) {
- return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
- };
- this.set('ua', ua)
- .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
- .set(UA_CPU, createUAParserItemWithCH(UA_CPU))
- .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
- .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
- .set(UA_OS, createUAParserItemWithCH(UA_OS));
- }
- return this;
- };
- UAParserItem.prototype.set = function (prop, val) {
- this.data[prop] = val;
- return this;
- };
function UAParser (ua, extensions, headers) {
@@ -1094,26 +1104,44 @@ const window = undefined;
extend(defaultRegexes, extensions) :
defaultRegexes,
- createUAParserItemFunc = function (itemType) {
- return function () {
- return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get();
- };
+ createItemFunc = function (itemType) {
+ if (itemType == UA_RESULT) {
+ return function () {
+ return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH)
+ .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 UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
+ .parseUA()
+ .detectFeature()
+ .get();
+ };
+ }
};
// public methods
- assignFromEntries.call(this, [
- ['getBrowser', createUAParserItemFunc(UA_BROWSER)],
- ['getCPU', createUAParserItemFunc(UA_CPU)],
- ['getDevice', createUAParserItemFunc(UA_DEVICE)],
- ['getEngine', createUAParserItemFunc(UA_ENGINE)],
- ['getOS', createUAParserItemFunc(UA_OS)],
- ['getResult', createUAParserItemFunc(UA_RESULT)],
+ 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) {
userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
return this;
}]
- ]).setUA(userAgent);
+ ])
+ .setUA(userAgent);
+
return this;
}
diff --git a/src/enum/ua-parser-enum.js b/src/ua-parser-enum.js
similarity index 98%
rename from src/enum/ua-parser-enum.js
rename to src/ua-parser-enum.js
index 9276b9ace..6d88e813f 100644
--- a/src/enum/ua-parser-enum.js
+++ b/src/ua-parser-enum.js
@@ -5,6 +5,8 @@
MIT License */
//////////////////////////////////////////////
+/*jshint esversion: 6 */
+
const BrowserName = Object.freeze({
CHROME : 'Chrome',
EDGE : 'Edge',
@@ -98,4 +100,4 @@ module.exports = {
DeviceVendor,
EngineName,
OSName
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/extension/ua-parser-extension.js b/src/ua-parser-extension.js
similarity index 99%
rename from src/extension/ua-parser-extension.js
rename to src/ua-parser-extension.js
index 8b1036636..fed987eb3 100644
--- a/src/extension/ua-parser-extension.js
+++ b/src/ua-parser-extension.js
@@ -5,6 +5,8 @@
MIT License */
//////////////////////////////////////////////
+/*jshint esversion: 6 */
+
const MODEL = 'model';
const NAME = 'name';
const TYPE = 'type';
@@ -117,4 +119,4 @@ module.exports = {
ExtraDevices,
Emails,
Tools
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/ua-parser.js b/src/ua-parser.js
index ffa73eaa0..1e04b5940 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -8,7 +8,8 @@
Source : https://github.com/faisalman/ua-parser-js */
/////////////////////////////////////////////////////////////////////////////////
-/*jshint esversion: 3 */
+/* jshint esversion: 3 */
+/* globals window */
(function (window, undefined) {
diff --git a/test/browser-test.json b/test/specs/browser-all.json
similarity index 100%
rename from test/browser-test.json
rename to test/specs/browser-all.json
diff --git a/test/cpu-test.json b/test/specs/cpu-all.json
similarity index 100%
rename from test/cpu-test.json
rename to test/specs/cpu-all.json
diff --git a/test/device-test.json b/test/specs/device-all.json
similarity index 100%
rename from test/device-test.json
rename to test/specs/device-all.json
diff --git a/test/engine-test.json b/test/specs/engine-all.json
similarity index 100%
rename from test/engine-test.json
rename to test/specs/engine-all.json
diff --git a/test/test-extension.json b/test/specs/extension-device.json
similarity index 100%
rename from test/test-extension.json
rename to test/specs/extension-device.json
diff --git a/test/mediaplayer-test.json b/test/specs/extension-mediaplayer.json
similarity index 100%
rename from test/mediaplayer-test.json
rename to test/specs/extension-mediaplayer.json
diff --git a/test/os-test.json b/test/specs/os-all.json
similarity index 100%
rename from test/os-test.json
rename to test/specs/os-all.json
diff --git a/test/test.js b/test/test.js
index a7dbeea43..c998088b7 100644
--- a/test/test.js
+++ b/test/test.js
@@ -5,11 +5,11 @@ var requirejs = require('requirejs');
var parseJS = require('@babel/parser').parse;
var traverse = require('@babel/traverse').default;
var UAParser = require('./../src/ua-parser');
-var browsers = require('./browser-test.json');
-var cpus = require('./cpu-test.json');
-var devices = require('./device-test.json');
-var engines = require('./engine-test.json');
-var os = require('./os-test.json');
+var browsers = require('./specs/browser-all.json');
+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 +82,6 @@ describe('Returns', function () {
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 },
cpu: { architecture: undefined },
device: { vendor: undefined, model: undefined, type: undefined },
@@ -362,43 +361,6 @@ describe('Map UA-CH headers', function () {
let engine = new UAParser(headers).getEngine().withClientHints();
let os = new UAParser(headers).getOS().withClientHints();
-/* let ua_ch = {
- "architecture": "ARM",
- "bitness": "64",
- "brands": [
- {
- "brand": "Chromium",
- "version": "93"
- },
- {
- "brand": "Google Chrome",
- "version": "93"
- },
- {
- "brand": " Not;A Brand",
- "version": "99"
- }
- ],
- "fullVersionList": [
- {
- "brand": "Chromium",
- "version": "93.0.1.2"
- },
- {
- "brand": "Google Chrome",
- "version": "93.0.1.2"
- },
- {
- "brand": " Not;A Brand",
- "version": "99.0.1.2"
- }
- ],
- "mobile": true,
- "model": "Pixel 99",
- "platform": "Windows",
- "platformVersion": "13"
- };*/
-
it('Can read from client-hints headers using `withClientHints()`', function () {
//assert.deepEqual(uap.ua_ch, ua_ch);
@@ -436,7 +398,6 @@ describe('Map UA-CH headers', function () {
engine = new UAParser(headers).getEngine();
os = new UAParser(headers).getOS();
- //assert.deepEqual(uap.ua_ch, ua_ch);
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "110.0.0.0");
assert.strictEqual(uap.browser.major, "110");
@@ -459,18 +420,6 @@ describe('Map UA-CH headers', function () {
uap = UAParser(headers2).withClientHints();
-/* ua_ch = {
- "architecture": undefined,
- "bitness": undefined,
- "brands": undefined,
- "fullVersionList": undefined,
- "mobile": true,
- "model": undefined,
- "platform": undefined,
- "platformVersion": undefined
- };
-
- assert.deepEqual(uap.ua_ch, ua_ch);*/
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "110.0.0.0");
assert.strictEqual(uap.browser.major, "110");
@@ -513,70 +462,4 @@ describe('Map UA-CH headers', function () {
assert.strictEqual(ua.device.is("tablet"), false);
});
});
-});
-
-describe('Map UA-CH JS', () => {
-
- it('does not throw when using withClientHints() in non-supported environment', () => {
- assert.doesNotThrow(() => {
- new UAParser().getResult().withClientHints();
- });
- });
-
- window = { navigator : { userAgent : '' , userAgentData : new NavigatorUAData() } };
- function NavigatorUAData () {
- this.mobile = false;
- this.platform = '';
- this.brands = [{ brand : 'A Chromium-based Browser', version : '1' }];
- this.getHighEntropyValues = (values) => {
- return new Promise((resolve) => {
- const result = {
- brands : [{ brand : 'A Chromium-based Browser', version : '1' }],
- fullVersionList : [{ brand : 'A Chromium-based Browser', version : '1.2.3' }],
- architecture : 'x86',
- bitness : '64',
- mobile : true,
- model : 'Galaxy S3',
- platform : 'Android',
- platformVersion : '1000'
- };
- resolve(result);
- });
- }
- };
- delete require.cache[require.resolve('./../src/ua-parser')];
- const UAParserWithWindow = require('./../src/ua-parser');
-
- it('Can read client hints from browser', async () => {
-
- let uap = new UAParserWithWindow()
-
- let os = await uap.getOS().withClientHints();
-
- assert.strictEqual(os.name, 'Android');
- assert.strictEqual(os.is('Android'), true);
- assert.strictEqual(os.toString(), 'Android 1000');
-
- let result = await uap.getResult().withClientHints();
-
- assert.strictEqual(result.browser.name, 'A Chromium-based Browser');
- assert.strictEqual(result.browser.version, '1.2.3');
- assert.strictEqual(result.cpu.architecture, 'amd64');
- assert.strictEqual(result.os.name, 'Android');
-
- await uap.getDevice().withClientHints().then((device) => {
- assert.strictEqual(device.type, 'mobile');
- assert.strictEqual(device.vendor, undefined);
- assert.strictEqual(device.model, 'Galaxy S3');
- });
-
- let result_without_ch = uap.getResult();
-
- assert.strictEqual(result_without_ch.browser.name, undefined);
-
- uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");
- assert.strictEqual(uap.getOS().name, "macOS");
-
- // TODO : create full tests
- });
});
\ No newline at end of file
From 05747dba370731a56959935295679ccf1f6e76f2 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 8 Apr 2023 09:26:07 +0700
Subject: [PATCH 070/388] Install `@playwright/test` to perform test in real
browser
---
.gitignore | 1 +
package.json | 5 ++-
script/build-module.js | 1 +
test/{test-es6.mjs => mocha-test-es6.mjs} | 0
...t-extension.js => mocha-test-extension.js} | 0
test/{test.js => mocha-test.js} | 2 +-
test/playwright-test-browser.spec.mjs | 44 +++++++++++++++++++
7 files changed, 50 insertions(+), 3 deletions(-)
rename test/{test-es6.mjs => mocha-test-es6.mjs} (100%)
rename test/{test-extension.js => mocha-test-extension.js} (100%)
rename test/{test.js => mocha-test.js} (99%)
create mode 100644 test/playwright-test-browser.spec.mjs
diff --git a/.gitignore b/.gitignore
index ca8c34e70..0cd892f4d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
node_modules/
npm-debug.log
+playwright-report/
### vim ###
.*.s[a-w][a-z]
diff --git a/package.json b/package.json
index 259988cca..faba6cc57 100644
--- a/package.json
+++ b/package.json
@@ -164,12 +164,13 @@
],
"scripts": {
"build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js",
- "test": "jshint src/ && mocha -R nyan test",
- "test-ci": "jshint src/ && mocha -R spec test"
+ "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test",
+ "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test"
},
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
+ "@playwright/test": "^1.32.2",
"jshint": "~2.12.0",
"mocha": "~8.2.0",
"requirejs": "^2.3.2",
diff --git a/script/build-module.js b/script/build-module.js
index e002e55c7..5b47349dc 100644
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -1,3 +1,4 @@
+#!/usr/bin/env node
const fs = require('fs');
/*///////////////
diff --git a/test/test-es6.mjs b/test/mocha-test-es6.mjs
similarity index 100%
rename from test/test-es6.mjs
rename to test/mocha-test-es6.mjs
diff --git a/test/test-extension.js b/test/mocha-test-extension.js
similarity index 100%
rename from test/test-extension.js
rename to test/mocha-test-extension.js
diff --git a/test/test.js b/test/mocha-test.js
similarity index 99%
rename from test/test.js
rename to test/mocha-test.js
index c998088b7..e85322dd6 100644
--- a/test/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('./../src/ua-parser');
+var UAParser = require('../src/ua-parser');
var browsers = require('./specs/browser-all.json');
var cpus = require('./specs/cpu-all.json');
var devices = require('./specs/device-all.json');
diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-browser.spec.mjs
new file mode 100644
index 000000000..a8efd2265
--- /dev/null
+++ b/test/playwright-test-browser.spec.mjs
@@ -0,0 +1,44 @@
+// @ts-check
+import { test, expect } from '@playwright/test';
+import path from 'path';
+import url from 'url';
+
+test('read client hints data', async ({ page }) => {
+ await page.addInitScript(() => {
+ Object.defineProperty(navigator, 'userAgentData', {
+ value : {
+ brands : [],
+ platform : '',
+ mobile : false,
+ getHighEntropyValues : () => {
+ return Promise.resolve({
+ brands : [
+ {
+ brand : 'Chromium',
+ version : '110'
+ },
+ {
+ brand : 'Not(A:Brand',
+ version : '110'
+ },
+ {
+ brand : 'New Browser',
+ version : '110'
+ }
+ ],
+ platform : 'New OS'
+ });
+ }
+ }
+ });
+ });
+
+ const dirname = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../');
+ await page.goto(`file://${dirname}/dist/ua-parser.html`);
+
+ // @ts-ignore
+ const uap = await page.evaluate(async () => await UAParser().withClientHints());
+
+ expect(uap).toHaveProperty('browser.name', 'New Browser');
+ expect(uap).toHaveProperty('os.name', 'New OS');
+});
\ No newline at end of file
From 16b416d9ead3853e0a3bd73e39ad779e22fd7d38 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 9 Apr 2023 05:34:02 +0700
Subject: [PATCH 071/388] Move feature detection into its own method:
`withFeatureCheck`
---
.gitignore | 1 +
changelog.md | 1 +
readme.md | 15 +++++
src/ua-parser.js | 22 ++++++-
test/playwright-test-browser.spec.mjs | 89 +++++++++++++++++----------
5 files changed, 91 insertions(+), 37 deletions(-)
diff --git a/.gitignore b/.gitignore
index 0cd892f4d..4a99144dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
node_modules/
npm-debug.log
playwright-report/
+test-results/
### vim ###
.*.s[a-w][a-z]
diff --git a/changelog.md b/changelog.md
index 76d4ef8ec..ac8e493be 100644
--- a/changelog.md
+++ b/changelog.md
@@ -7,6 +7,7 @@
- What's new:
- Add some new methods in result object:
- Add support for client hints: `withClientHints()`
+ - Add support for feature detection: `withFeatureCheck()`
- Utility for easy comparison: `is()`
- Utility to print full-name: `toString()`
- Add support for ES module `import { UAParser } from 'ua-parser-js'`
diff --git a/readme.md b/readme.md
index 999898593..b73df8d65 100644
--- a/readme.md
+++ b/readme.md
@@ -346,6 +346,21 @@ new UAParser(request.headers)
});
```
+#### * `withFeatureCheck():object` `since@2.0`
+
+This method allows us to examine other features beyond `navigator.userAgent` to further improve detection of the following:
+- browser : Brave (check for `navigator.isBrave`)
+- device : iPad (check for `navigator.standalone` & `navigator.maxTouchPoints`)
+
+```js
+// suppose this code runs on iPad
+const withoutFeatureCheck = UAParser();
+const withFeatureCheck = UAParser().withFeatureCheck();
+
+console.log(withoutFeatureCheck.device); // { vendor : "Apple", model : "Macintosh", type : undefined }
+console.log(withFeatureCheck.device); // { vendor : "Apple", model : "iPad", type : "tablet" }
+```
+
## Extending Regex
If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own.
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 1e04b5940..68ac98d66 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -862,6 +862,10 @@
});
};
+ UAParserData.prototype.withFeatureCheck = function () {
+ return item.detectFeature().get();
+ };
+
if (itemType != UA_RESULT) {
UAParserData.prototype.is = function (strToCheck) {
var is = false;
@@ -978,6 +982,20 @@
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;
@@ -1046,8 +1064,7 @@
.parseCH()
.get();
};
- this.set('ua', ua)
- .set(UA_BROWSER, parse(UA_BROWSER))
+ 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))
@@ -1118,7 +1135,6 @@
return function () {
return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
.parseUA()
- .detectFeature()
.get();
};
}
diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-browser.spec.mjs
index a8efd2265..a3fb5b890 100644
--- a/test/playwright-test-browser.spec.mjs
+++ b/test/playwright-test-browser.spec.mjs
@@ -3,42 +3,63 @@ import { test, expect } from '@playwright/test';
import path from 'path';
import url from 'url';
+const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../')}/dist/ua-parser.html`;
+
test('read client hints data', async ({ page }) => {
- await page.addInitScript(() => {
- Object.defineProperty(navigator, 'userAgentData', {
- value : {
- brands : [],
- platform : '',
- mobile : false,
- getHighEntropyValues : () => {
- return Promise.resolve({
- brands : [
- {
- brand : 'Chromium',
- version : '110'
- },
- {
- brand : 'Not(A:Brand',
- version : '110'
- },
- {
- brand : 'New Browser',
- version : '110'
- }
- ],
- platform : 'New OS'
- });
- }
- }
+ await page.addInitScript(() => {
+ Object.defineProperty(navigator, 'userAgentData', {
+ value: {
+ brands: [],
+ platform: '',
+ mobile: false,
+ getHighEntropyValues: () => {
+ return Promise.resolve({
+ brands: [
+ {
+ brand: 'Chromium',
+ version: '110'
+ },
+ {
+ brand: 'Not(A:Brand',
+ version: '110'
+ },
+ {
+ brand: 'New Browser',
+ version: '110'
+ }
+ ],
+ platform: 'New OS'
+ });
+ }
+ }
+ });
});
- });
- const dirname = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../');
- await page.goto(`file://${dirname}/dist/ua-parser.html`);
+ await page.goto(localHtml);
+
+ // @ts-ignore
+ const uap = await page.evaluate(async () => await UAParser().withClientHints());
+
+ expect(uap).toHaveProperty('browser.name', 'New Browser');
+ expect(uap).toHaveProperty('os.name', 'New OS');
+});
+
+test('detect Brave', async ({ page }) => {
+ await page.addInitScript(() => {
+ Object.defineProperty(navigator, 'brave', {
+ value: {
+ isBrave: () => true
+ }
+ });
+ });
+
+ await page.goto(localHtml);
+
+ // @ts-ignore
+ let uap = await page.evaluate(() => UAParser());
+ expect(uap).toHaveProperty('browser.name', 'Chrome Headless');
- // @ts-ignore
- const uap = await page.evaluate(async () => await UAParser().withClientHints());
-
- expect(uap).toHaveProperty('browser.name', 'New Browser');
- expect(uap).toHaveProperty('os.name', 'New OS');
+ // @ts-ignore
+ uap = await page.evaluate(() => UAParser().withFeatureCheck());
+ expect(uap).toHaveProperty('browser.name', 'Brave');
});
\ No newline at end of file
From 407b23262ce0f792e892fafdbfc8569702f52d2d Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 9 Apr 2023 09:04:48 +0700
Subject: [PATCH 072/388] Update actions to v3
---
.github/workflows/run-test.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml
index 203184294..5ce36be7d 100644
--- a/.github/workflows/run-test.yml
+++ b/.github/workflows/run-test.yml
@@ -12,8 +12,8 @@ jobs:
matrix:
arch: [amd64, ppc64le]
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
- name: Run the test
run: |
npm install
From 07c9e36ebedb0b6a0935150375d9c9af40daeee8 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 9 Apr 2023 10:13:08 +0700
Subject: [PATCH 073/388] Update actions to install playwright & run build
before test
---
.github/workflows/run-test.yml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml
index 5ce36be7d..7a4cc58ce 100644
--- a/.github/workflows/run-test.yml
+++ b/.github/workflows/run-test.yml
@@ -14,7 +14,12 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
+ with:
+ node-version: 'lts/*'
+ cache: 'npm'
- name: Run the test
run: |
npm install
+ npm run build
+ npx playwright install
npm run test-ci
From a8951ec282ad27df8274c165d5f41ac0ad20f7c1 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 9 Apr 2023 10:21:20 +0700
Subject: [PATCH 074/388] Update actions to remove cache
---
.github/workflows/run-test.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml
index 7a4cc58ce..4ed93e946 100644
--- a/.github/workflows/run-test.yml
+++ b/.github/workflows/run-test.yml
@@ -16,7 +16,6 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 'lts/*'
- cache: 'npm'
- name: Run the test
run: |
npm install
From 801c2409b3e0fb83f67e95ebeca2723adb4976c9 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 9 Apr 2023 15:54:46 +0700
Subject: [PATCH 075/388] Update readme & IData explanations
---
dist/ua-parser.min.js | 2 +-
dist/ua-parser.pack.js | 2 +-
readme.md | 125 +++++++++++++++++++++++++--------------
src/module/ua-parser.mjs | 62 ++++++++++++-------
src/ua-parser.js | 42 ++++++-------
5 files changed, 143 insertions(+), 90 deletions(-)
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index d5b153cca..e18ba06a7 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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])}}}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 ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set("ua",ua).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",createUAParserData(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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAParserItem(itemType,userAgent,regexMap,HTTP_UACH).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 UAParserItem(itemType,userAgent,regexMap[itemType],HTTP_UACH).parseUA().detectFeature().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){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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-alpha.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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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 ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=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,HTTP_UACH).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],HTTP_UACH).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){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 141057c29..59ce392a0 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.2
Copyright © 2012-2023 Faisal Salman
MIT License */
-!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
+!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
diff --git a/readme.md b/readme.md
index b73df8d65..ed4c749d0 100644
--- a/readme.md
+++ b/readme.md
@@ -3,11 +3,13 @@
-
-
-
-
-
+
+
+
+
+
+
+
# UAParser.js
@@ -47,39 +49,47 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
What's new & breaking, please read [CHANGELOG](changelog.md) before upgrading.
# Documentation
-### UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])
-
-In the browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in' nodejs for the function to work. Usually you can find the user agent in: `request.headers["user-agent"]`.
+### `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData`
+In browser environment you don't need to pass the user-agent string to the function, as it should automatically get the string from the `window.navigator.userAgent`. Whereas in nodejs environment, the user-agent string must be passed in order for the function to work (usually you can find the user-agent in: `request.headers["user-agent"]`).
## Constructor
+
+#### * `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):UAParser`
+
When you call `UAParser` with the `new` keyword, `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string.
Like so:
-* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])`
+
```js
let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs
console.log(parser); // {}
let parserResults = parser.getResult();
console.log(parserResults);
-/** {
- "ua" : "",
- "browser" : {},
- "engine" : {},
- "os" : {},
- "device" : {},
- "cpu" : {}
-} */
+/*
+ {
+ ua : "",
+ browser : {},
+ engine : {},
+ os : {},
+ device : {},
+ cpu : {}
+ }
+*/
```
-When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
-* `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])`
- * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
+#### * `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData`
+
+When you call `UAParser` without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
+
+```sh
+returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
+```
-## Methods
+## `UAParser`:
#### Methods table
The methods are self explanatory, here's a small overview on all the available methods:
-* `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os:
+ * `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os:
`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`.
* `getBrowser()` - returns the browser name and version.
@@ -88,17 +98,21 @@ The methods are self explanatory, here's a small overview on all the available m
* `getOS()` - returns the running operating system name and version.
* `getCPU()` - returns CPU architectural design name.
* `getUA()` - returns the user-agent string.
- * `setUA(user-agent)` - set a custom user-agent to be parsed.
+ * `setUA(ua)` - set a custom user-agent to be parsed.
---
-* `getResult()`
- * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
+#### * `getResult():IData`
+
+```sh
+returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
+```
-* `getBrowser()`
- * returns `{ name: '', version: '' }`
+#### * `getBrowser():IData`
```sh
+returns `{ name: '', version: '' }`
+
# Possible 'browser.name':
2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG,
BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera,
@@ -119,10 +133,11 @@ Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ...
# 'browser.version' determined dynamically
```
-* `getDevice()`
- * returns `{ model: '', type: '', vendor: '' }`
+#### * `getDevice():IData`
```sh
+returns `{ model: '', type: '', vendor: '' }`
+
# Possible 'device.type':
console, mobile, tablet, smarttv, wearable, embedded
@@ -143,10 +158,11 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE
# 'device.model' determined dynamically
```
-* `getEngine()`
- * returns `{ name: '', version: '' }`
+#### * `getEngine():IData`
```sh
+returns `{ name: '', version: '' }`
+
# Possible 'engine.name'
Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, LibWeb, Links, Lynx,
NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit
@@ -154,10 +170,11 @@ NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit
# 'engine.version' determined dynamically
```
-* `getOS()`
- * returns `{ name: '', version: '' }`
+#### * `getOS():IData`
```sh
+returns `{ name: '', version: '' }`
+
# Possible 'os.name'
AIX, Amiga OS, Android[-x86], Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS,
Contiki, Fedora, Firefox OS, FreeBSD, Debian, Deepin, DragonFly, elementary OS,
@@ -172,22 +189,42 @@ Zenwalk, ...
# 'os.version' determined dynamically
```
-* `getCPU()`
- * returns `{ architecture: '' }`
+#### * `getCPU():IData`
```sh
+returns `{ architecture: '' }`
+
# Possible 'cpu.architecture'
68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64]
```
-* `getUA()`
- * returns UA string of current instance
+#### * `getUA():string`
+
+```sh
+returns user-agent string of current instance
+```
+
+#### * `setUA(ua:string):UAParser`
+
+```sh
+set user-agent string to be parsed
+returns current instance
+```
-* `setUA(uastring)`
- * set UA string to be parsed
- * returns current instance
+---
+
+## `IData`: `since@2.0`
+
+#### Methods table
+The methods are self explanatory, here's a small overview on all the available methods:
+ * `is(value)` - returns `true` if the passed value matches a value of current object, `false` otherwise
+ * `toString()` - returns the full-name values of current object as a string
+ * `withClientHints()` - returns an object with re-updated data from client hints
+ * `withFeatureCheck()` - returns an object with re-updated data from feature detection
+
+---
-#### * `is():boolean` utility `since@2.0`
+#### * `is(value:string):boolean`
```js
// Is just a shorthand comparison to check whether the value of specified item equals one of its properties (in a case-insensitive way)
@@ -246,7 +283,7 @@ let engine = uap.getEngine();
engine.is("Blink"); // true
```
-#### * `toString():string` utility `since@2.0`
+#### * `toString():string`
```js
// Retrieve full-name values as a string
@@ -287,7 +324,7 @@ engine.version; // "28.0.1500.95"
engine.toString(); // "Blink 28.0.1500.95"
```
-#### * `withClientHints():Promise|Thenable|object` `since@2.0`
+#### * `withClientHints():Promise|Thenable|IData`
Recently, Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`.
@@ -346,7 +383,7 @@ new UAParser(request.headers)
});
```
-#### * `withFeatureCheck():object` `since@2.0`
+#### * `withFeatureCheck():IData`
This method allows us to examine other features beyond `navigator.userAgent` to further improve detection of the following:
- browser : Brave (check for `navigator.isBrave`)
diff --git a/src/module/ua-parser.mjs b/src/module/ua-parser.mjs
index ba554e354..250eea72a 100644
--- a/src/module/ua-parser.mjs
+++ b/src/module/ua-parser.mjs
@@ -829,22 +829,22 @@
return props;
})();
- var createUAParserData = function (item, itemType) {
+ var createIData = function (item, itemType) {
var init_props = defaultProps.init[itemType],
is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0,
toString_props = defaultProps.toString[itemType] || 0;
- function UAParserData () {
+ function IData () {
setProps.call(this, init_props);
}
- UAParserData.prototype.getItem = function () {
+ IData.prototype.getItem = function () {
return item;
};
- UAParserData.prototype.withClientHints = function () {
+ IData.prototype.withClientHints = function () {
// nodejs / non-client-hints browsers
if (!NAVIGATOR_UADATA) {
@@ -858,14 +858,18 @@
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
return item
- .setCH(new UAParserDataCH(res, false))
+ .setCH(new UACHData(res, false))
.parseCH()
.get();
});
};
+ IData.prototype.withFeatureCheck = function () {
+ return item.detectFeature().get();
+ };
+
if (itemType != UA_RESULT) {
- UAParserData.prototype.is = function (strToCheck) {
+ IData.prototype.is = function (strToCheck) {
var is = false;
for (var i in this) {
if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
@@ -878,7 +882,7 @@
}
return is;
};
- UAParserData.prototype.toString = function () {
+ IData.prototype.toString = function () {
var str = EMPTY;
for (var i in toString_props) {
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
@@ -890,33 +894,33 @@
}
if (!NAVIGATOR_UADATA) {
- UAParserData.prototype.then = function (cb) {
+ IData.prototype.then = function (cb) {
var that = this;
- var UAParserDataResolve = function () {
+ var IDataResolve = function () {
for (var prop in that) {
if (that.hasOwnProperty(prop)) {
this[prop] = that[prop];
}
}
};
- UAParserDataResolve.prototype = {
- is : UAParserData.prototype.is,
- toString : UAParserData.prototype.toString
+ IDataResolve.prototype = {
+ is : IData.prototype.is,
+ toString : IData.prototype.toString
};
- var resolveData = new UAParserDataResolve();
+ var resolveData = new IDataResolve();
cb(resolveData);
return resolveData;
};
}
- return new UAParserData();
+ return new IData();
};
/////////////////
// Constructor
////////////////
- function UAParserDataCH (uach, isHTTP_UACH) {
+ function UACHData (uach, isHTTP_UACH) {
uach = uach || {};
setProps.call(this, CH_ALL_VALUES);
if (isHTTP_UACH) {
@@ -938,7 +942,7 @@
}
}
- function UAParserItem (itemType, ua, rgxMap, uaCH) {
+ function UAItem (itemType, ua, rgxMap, uaCH) {
this.get = function (prop) {
if (!prop) return this.data;
@@ -980,6 +984,20 @@
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;
@@ -1048,8 +1066,7 @@
.parseCH()
.get();
};
- this.set('ua', ua)
- .set(UA_BROWSER, parse(UA_BROWSER))
+ 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))
@@ -1063,7 +1080,7 @@
['ua', ua],
['uaCH', uaCH],
['rgxMap', rgxMap],
- ['data', createUAParserData(this, itemType)]
+ ['data', createIData(this, itemType)]
]);
return this;
@@ -1098,7 +1115,7 @@
headers[USER_AGENT] :
EMPTY)),
- HTTP_UACH = new UAParserDataCH(headers, true),
+ HTTP_UACH = new UACHData(headers, true),
regexMap = extensions ?
extend(defaultRegexes, extensions) :
@@ -1107,7 +1124,7 @@
createItemFunc = function (itemType) {
if (itemType == UA_RESULT) {
return function () {
- return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap, HTTP_UACH)
.set('ua', userAgent)
.set(UA_BROWSER, this.getBrowser())
.set(UA_CPU, this.getCPU())
@@ -1118,9 +1135,8 @@
};
} else {
return function () {
- return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
.parseUA()
- .detectFeature()
.get();
};
}
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 68ac98d66..d72fda7bb 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -827,22 +827,22 @@
return props;
})();
- var createUAParserData = function (item, itemType) {
+ var createIData = function (item, itemType) {
var init_props = defaultProps.init[itemType],
is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0,
toString_props = defaultProps.toString[itemType] || 0;
- function UAParserData () {
+ function IData () {
setProps.call(this, init_props);
}
- UAParserData.prototype.getItem = function () {
+ IData.prototype.getItem = function () {
return item;
};
- UAParserData.prototype.withClientHints = function () {
+ IData.prototype.withClientHints = function () {
// nodejs / non-client-hints browsers
if (!NAVIGATOR_UADATA) {
@@ -856,18 +856,18 @@
.getHighEntropyValues(CH_ALL_VALUES)
.then(function (res) {
return item
- .setCH(new UAParserDataCH(res, false))
+ .setCH(new UACHData(res, false))
.parseCH()
.get();
});
};
- UAParserData.prototype.withFeatureCheck = function () {
+ IData.prototype.withFeatureCheck = function () {
return item.detectFeature().get();
};
if (itemType != UA_RESULT) {
- UAParserData.prototype.is = function (strToCheck) {
+ IData.prototype.is = function (strToCheck) {
var is = false;
for (var i in this) {
if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
@@ -880,7 +880,7 @@
}
return is;
};
- UAParserData.prototype.toString = function () {
+ IData.prototype.toString = function () {
var str = EMPTY;
for (var i in toString_props) {
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
@@ -892,33 +892,33 @@
}
if (!NAVIGATOR_UADATA) {
- UAParserData.prototype.then = function (cb) {
+ IData.prototype.then = function (cb) {
var that = this;
- var UAParserDataResolve = function () {
+ var IDataResolve = function () {
for (var prop in that) {
if (that.hasOwnProperty(prop)) {
this[prop] = that[prop];
}
}
};
- UAParserDataResolve.prototype = {
- is : UAParserData.prototype.is,
- toString : UAParserData.prototype.toString
+ IDataResolve.prototype = {
+ is : IData.prototype.is,
+ toString : IData.prototype.toString
};
- var resolveData = new UAParserDataResolve();
+ var resolveData = new IDataResolve();
cb(resolveData);
return resolveData;
};
}
- return new UAParserData();
+ return new IData();
};
/////////////////
// Constructor
////////////////
- function UAParserDataCH (uach, isHTTP_UACH) {
+ function UACHData (uach, isHTTP_UACH) {
uach = uach || {};
setProps.call(this, CH_ALL_VALUES);
if (isHTTP_UACH) {
@@ -940,7 +940,7 @@
}
}
- function UAParserItem (itemType, ua, rgxMap, uaCH) {
+ function UAItem (itemType, ua, rgxMap, uaCH) {
this.get = function (prop) {
if (!prop) return this.data;
@@ -1078,7 +1078,7 @@
['ua', ua],
['uaCH', uaCH],
['rgxMap', rgxMap],
- ['data', createUAParserData(this, itemType)]
+ ['data', createIData(this, itemType)]
]);
return this;
@@ -1113,7 +1113,7 @@
headers[USER_AGENT] :
EMPTY)),
- HTTP_UACH = new UAParserDataCH(headers, true),
+ HTTP_UACH = new UACHData(headers, true),
regexMap = extensions ?
extend(defaultRegexes, extensions) :
@@ -1122,7 +1122,7 @@
createItemFunc = function (itemType) {
if (itemType == UA_RESULT) {
return function () {
- return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap, HTTP_UACH)
.set('ua', userAgent)
.set(UA_BROWSER, this.getBrowser())
.set(UA_CPU, this.getCPU())
@@ -1133,7 +1133,7 @@
};
} else {
return function () {
- return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
.parseUA()
.get();
};
From 2fb0c728981efd44a14001a2de901182b80066af Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 11 Apr 2023 10:59:46 +0700
Subject: [PATCH 076/388] Create deploy-docs.yml
---
.github/workflows/deploy-docs.yml | 33 +++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 .github/workflows/deploy-docs.yml
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
new file mode 100644
index 000000000..b6c547aa7
--- /dev/null
+++ b/.github/workflows/deploy-docs.yml
@@ -0,0 +1,33 @@
+name: Deploy VitePress
+on:
+ workflow_dispatch: {}
+ push:
+ branches:
+ - gh-pages
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 16
+ cache: npm
+ - run: npm ci
+ - name: Build
+ run: npm run docs:build
+ - uses: actions/configure-pages@v2
+ - uses: actions/upload-pages-artifact@v1
+ with:
+ path: docs/v2/.vitepress/dist
+ - name: Deploy
+ id: deployment
+ uses: actions/deploy-pages@v1
From e62cded08303c790a537ab8ba08bcd65258d15d4 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 11 Apr 2023 11:24:11 +0700
Subject: [PATCH 077/388] Update deploy-docs.yml
---
.github/workflows/deploy-docs.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index b6c547aa7..cb081e648 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -20,7 +20,6 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 16
- cache: npm
- run: npm ci
- name: Build
run: npm run docs:build
From ff268137086551cd0e95a4f2c99830f3bf3425dd Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 11 Apr 2023 11:26:11 +0700
Subject: [PATCH 078/388] Update deploy-docs.yml - again
---
.github/workflows/deploy-docs.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index cb081e648..6e716046e 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -20,7 +20,6 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 16
- - run: npm ci
- name: Build
run: npm run docs:build
- uses: actions/configure-pages@v2
From 46f38adb838796636f9429a58a72bd64aa2e8bc8 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 11 Apr 2023 23:06:26 +0700
Subject: [PATCH 079/388] Remove deploy-docs.yml - Move docs to another repo
---
.github/workflows/deploy-docs.yml | 31 -------------------------------
1 file changed, 31 deletions(-)
delete mode 100644 .github/workflows/deploy-docs.yml
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
deleted file mode 100644
index 6e716046e..000000000
--- a/.github/workflows/deploy-docs.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Deploy VitePress
-on:
- workflow_dispatch: {}
- push:
- branches:
- - gh-pages
-jobs:
- deploy:
- runs-on: ubuntu-latest
- permissions:
- pages: write
- id-token: write
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- steps:
- - uses: actions/checkout@v3
- with:
- fetch-depth: 0
- - uses: actions/setup-node@v3
- with:
- node-version: 16
- - name: Build
- run: npm run docs:build
- - uses: actions/configure-pages@v2
- - uses: actions/upload-pages-artifact@v1
- with:
- path: docs/v2/.vitepress/dist
- - name: Deploy
- id: deployment
- uses: actions/deploy-pages@v1
From 99baf60d50ae3a0e944689e4aa2f2e00211f6bcf Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 12 Apr 2023 11:48:00 +0700
Subject: [PATCH 080/388] Fix #608 - Add OpenSSF Scorecard GitHub Action
---
.github/workflows/scorecard.yml | 72 +++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 .github/workflows/scorecard.yml
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
new file mode 100644
index 000000000..5844bc19a
--- /dev/null
+++ b/.github/workflows/scorecard.yml
@@ -0,0 +1,72 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecard supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '45 2 * * 4'
+ push:
+ branches: [ "master" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecard analysis
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+ # Uncomment the permissions below if installing in a private repository.
+ # contents: read
+ # actions: read
+
+ steps:
+ - name: "Checkout code"
+ uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecard on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
+ repo_token: ${{ secrets.SCORECARD_TOKEN }}
+
+ # Public repositories:
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories:
+ # - `publish_results` will always be set to `false`, regardless
+ # of the value entered here.
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard.
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
+ with:
+ sarif_file: results.sarif
From f5af76a2b3a447c882fd6ddedf65b2f4f5c0f4d7 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 12 Apr 2023 13:16:41 +0700
Subject: [PATCH 081/388] Create GitHub packaging workflow
---
.../workflows/npm-publish-github-packages.yml | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 .github/workflows/npm-publish-github-packages.yml
diff --git a/.github/workflows/npm-publish-github-packages.yml b/.github/workflows/npm-publish-github-packages.yml
new file mode 100644
index 000000000..47a37f98c
--- /dev/null
+++ b/.github/workflows/npm-publish-github-packages.yml
@@ -0,0 +1,36 @@
+# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
+# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
+
+name: Node.js Package
+
+on:
+ release:
+ types: [created]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 16
+ - run: npm ci
+ - run: npm test
+
+ publish-gpr:
+ needs: build
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 16
+ registry-url: https://npm.pkg.github.com/
+ - run: npm ci
+ - run: npm publish
+ env:
+ NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
From c2f17004b862e9b692eeaa155cdd072b0c149072 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 13 Apr 2023 06:08:52 +0700
Subject: [PATCH 082/388] Add GitHub's CodeQL Action for static code analysis
---
.github/workflows/codeql.yml | 76 ++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 .github/workflows/codeql.yml
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 000000000..1db5e9812
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,76 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ "master"]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ "master" ]
+ schedule:
+ - cron: '15 6 * * 0'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'javascript' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Use only 'java' to analyze code written in Java, Kotlin or both
+ # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
+ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+
+ # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+ # queries: security-extended,security-and-quality
+
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ # - name: Autobuild
+ # uses: github/codeql-action/autobuild@v2
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+
+ # If the Autobuild fails above, remove it and uncomment the following three lines.
+ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
+
+ # - run: |
+ # echo "Run, Build Application using script"
+ # ./location_of_script_within_repo/buildscript.sh
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
+ with:
+ category: "/language:${{matrix.language}}"
From 432a2ee72d2354fea6c9d088bff54c453bcddf2b Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 13 Apr 2023 05:04:16 +0700
Subject: [PATCH 083/388] Pin dependency hash with lockfile & test with
lockfile-lint
---
.github/workflows/run-test.yml | 2 +-
.npmrc | 1 -
package-lock.json | 1813 ++++++++++++++++++++++++++++++++
package.json | 8 +-
4 files changed, 1818 insertions(+), 6 deletions(-)
delete mode 100644 .npmrc
create mode 100644 package-lock.json
diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml
index 4ed93e946..b3990c8f5 100644
--- a/.github/workflows/run-test.yml
+++ b/.github/workflows/run-test.yml
@@ -18,7 +18,7 @@ jobs:
node-version: 'lts/*'
- name: Run the test
run: |
- npm install
+ npm ci
npm run build
npx playwright install
npm run test-ci
diff --git a/.npmrc b/.npmrc
deleted file mode 100644
index 43c97e719..000000000
--- a/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-package-lock=false
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..d696d4bcb
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1813 @@
+{
+ "name": "ua-parser-js",
+ "version": "2.0.0-alpha.2",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "ua-parser-js",
+ "version": "2.0.0-alpha.2",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ua-parser-js"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/faisalman"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/faisalman"
+ }
+ ],
+ "license": "MIT",
+ "devDependencies": {
+ "@babel/parser": "7.15.8",
+ "@babel/traverse": "7.15.4",
+ "@playwright/test": "~1.32.2",
+ "jshint": "~2.12.0",
+ "mocha": "~8.2.0",
+ "requirejs": "2.3.2",
+ "safe-regex": "^2.1.1",
+ "uglify-js": "~3.12.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
+ "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
+ "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.21.4",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
+ "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.20.7",
+ "@babel/types": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+ "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+ "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+ "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.15.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz",
+ "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template/node_modules/@babel/parser": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.15.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz",
+ "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.14.5",
+ "@babel/generator": "^7.15.4",
+ "@babel/helper-function-name": "^7.15.4",
+ "@babel/helper-hoist-variables": "^7.15.4",
+ "@babel/helper-split-export-declaration": "^7.15.4",
+ "@babel/parser": "^7.15.4",
+ "@babel/types": "^7.15.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz",
+ "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.18",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.32.3",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz",
+ "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.32.3"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.15.11",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
+ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
+ "dev": true
+ },
+ "node_modules/@ungap/promise-all-settled": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
+ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+ "dev": true
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
+ "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.5.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.1.2"
+ }
+ },
+ "node_modules/cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==",
+ "dev": true,
+ "dependencies": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=0.2.5"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha512-duS7VP5pvfsNLDvL1O4VOEbw37AI3A4ZUQYemvDlnpGrNu9tprR7BYWpDYwC0Xia0Zxz5ZupdiIrUp0GH1aXfg==",
+ "dev": true,
+ "dependencies": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "node_modules/date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==",
+ "dev": true
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+ "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "entities": "^2.0.0"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "node_modules/domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha512-q9bUwjfp7Eif8jWxxxPSykdRZAb6GkguBGSgvvCrhI9wB71W2K/Kvv4E61CF/mcCfnVJDeDWx/Vb/uAqbDj6UQ==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "node_modules/entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==",
+ "dev": true
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.x"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha512-hBxEg3CYXe+rPIua8ETe7tmG3XDn9B0edOE/e9wH2nLczxzgdu0m0aNHY+5wFZiviLWLdANPJTssa92dMcXQ5Q==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/jshint": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz",
+ "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==",
+ "dev": true,
+ "dependencies": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "~4.17.19",
+ "minimatch": "~3.0.2",
+ "shelljs": "0.3.x",
+ "strip-json-comments": "1.0.x"
+ },
+ "bin": {
+ "jshint": "bin/jshint"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/log-symbols": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+ "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/log-symbols/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+ "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mocha": {
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz",
+ "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==",
+ "dev": true,
+ "dependencies": {
+ "@ungap/promise-all-settled": "1.1.2",
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.4.3",
+ "debug": "4.2.0",
+ "diff": "4.0.2",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.1.6",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.14.0",
+ "log-symbols": "4.0.0",
+ "minimatch": "3.0.4",
+ "ms": "2.1.2",
+ "nanoid": "3.1.12",
+ "serialize-javascript": "5.0.1",
+ "strip-json-comments": "3.1.1",
+ "supports-color": "7.2.0",
+ "which": "2.0.2",
+ "wide-align": "1.1.3",
+ "workerpool": "6.0.2",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha"
+ },
+ "engines": {
+ "node": ">= 10.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mochajs"
+ }
+ },
+ "node_modules/mocha/node_modules/debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mocha/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mocha/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mocha/node_modules/nanoid": {
+ "version": "3.1.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz",
+ "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/mocha/node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.32.3",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.3.tgz",
+ "integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==",
+ "dev": true,
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
+ "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/regexp-tree": {
+ "version": "0.1.24",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz",
+ "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==",
+ "dev": true,
+ "bin": {
+ "regexp-tree": "bin/regexp-tree"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "node_modules/requirejs": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.2.tgz",
+ "integrity": "sha512-rGSBJSIWfUEkMTztpZlyGIpRIwuMau6gZYxQhxH1jCggR3ddWsI8+/aWh9yUscMB1O0pEgZrJiXy2hJerzMszA==",
+ "dev": true,
+ "bin": {
+ "r_js": "bin/r.js",
+ "r.js": "bin/r.js"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safe-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
+ "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
+ "dev": true,
+ "dependencies": {
+ "regexp-tree": "~0.1.1"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+ "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dev": true
+ },
+ "node_modules/shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+ "integrity": "sha512-Ny0KN4dyT8ZSCE0frtcbAJGoM/HTArpyPkeli1/00aYfm0sbD/Gk/4x7N2DP9QKGpBsiQH7n6rpm1L79RtviEQ==",
+ "dev": true,
+ "bin": {
+ "shjs": "bin/shjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "node_modules/string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
+ "dev": true
+ },
+ "node_modules/string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "dependencies": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg==",
+ "dev": true,
+ "bin": {
+ "strip-json-comments": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/uglify-js": {
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz",
+ "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==",
+ "dev": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true
+ },
+ "node_modules/wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "node_modules/workerpool": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz",
+ "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==",
+ "dev": true
+ },
+ "node_modules/wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
+ },
+ "node_modules/yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yargs/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs/node_modules/find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs/node_modules/locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yargs/node_modules/p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs/node_modules/path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index faba6cc57..451baa138 100644
--- a/package.json
+++ b/package.json
@@ -164,16 +164,16 @@
],
"scripts": {
"build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js",
- "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test",
- "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test"
+ "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json",
+ "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json"
},
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
- "@playwright/test": "^1.32.2",
+ "@playwright/test": "~1.32.2",
"jshint": "~2.12.0",
"mocha": "~8.2.0",
- "requirejs": "^2.3.2",
+ "requirejs": "2.3.2",
"safe-regex": "^2.1.1",
"uglify-js": "~3.12.0"
},
From 6c58ac39cd6b2d5497b4598f81254acce02023d8 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 13 Apr 2023 05:58:06 +0700
Subject: [PATCH 084/388] Update security policy
---
security.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/security.md b/security.md
index de525bbf5..d1f40fe80 100644
--- a/security.md
+++ b/security.md
@@ -2,4 +2,6 @@
## Reporting a Vulnerability
-Please report security issues to `f@faisalman.com`
+To report a security issue, please email `f@faisalman.com` with a description of the issue, reproducible steps to get the issue, affected versions, and, if known, mitigations for the issue.
+
+If the issue is confirmed as a vulnerability, we will open a new security advisory draft in our GitHub's Security Advisory page [https://github.com/faisalman/ua-parser-js/security/advisories](https://github.com/faisalman/ua-parser-js/security/advisories) and acknowledge your contributions as part of it. This project follows a 90 days disclosure timeline.
\ No newline at end of file
From f65965950047e928b549a4d39ae545e605d3ced1 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 13 Apr 2023 06:24:11 +0700
Subject: [PATCH 085/388] Set CodeQL permission to read-only
---
.github/workflows/codeql.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 1db5e9812..0f42cb1ff 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -20,6 +20,8 @@ on:
schedule:
- cron: '15 6 * * 0'
+permissions: read-all
+
jobs:
analyze:
name: Analyze
From f92bb9ef65eb34f3a8c74241477624e588ffa42f Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 13 Apr 2023 06:37:59 +0700
Subject: [PATCH 086/388] Insert scorecard badge & documentation link
---
readme.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/readme.md b/readme.md
index ed4c749d0..7b669d966 100644
--- a/readme.md
+++ b/readme.md
@@ -10,6 +10,7 @@
+
# UAParser.js
@@ -19,6 +20,7 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
* Author : Faisal Salman <>
* Demo : https://faisalman.github.io/ua-parser-js
* Source : https://github.com/faisalman/ua-parser-js
+* Documentation : https://faisalman.github.io/ua-parser-js-docs/v2
***
From 1b173159350b23c134cbf3735d9a6c93d686cae5 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 13 Apr 2023 06:56:27 +0700
Subject: [PATCH 087/388] Create action to review dependencies from PR
---
.github/workflows/dependency-review.yml | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 .github/workflows/dependency-review.yml
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
new file mode 100644
index 000000000..fe461b424
--- /dev/null
+++ b/.github/workflows/dependency-review.yml
@@ -0,0 +1,20 @@
+# Dependency Review Action
+#
+# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
+#
+# Source repository: https://github.com/actions/dependency-review-action
+# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
+name: 'Dependency Review'
+on: [pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ dependency-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout Repository'
+ uses: actions/checkout@v3
+ - name: 'Dependency Review'
+ uses: actions/dependency-review-action@v2
From 0ac502813779e45eb1181b1bec874dce2043eeaa Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 13 Apr 2023 07:07:34 +0700
Subject: [PATCH 088/388] Rename workflows for clarity
---
.github/workflows/{codeql.yml => analysis-codeql.yml} | 2 +-
.../{dependency-review.yml => analysis-dependency.yml} | 2 +-
.github/workflows/{scorecard.yml => analysis-scorecard.yml} | 2 +-
...-publish-github-packages.yml => publish-github-packages.yml} | 2 +-
.github/workflows/{run-test.yml => test-ci.yml} | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
rename .github/workflows/{codeql.yml => analysis-codeql.yml} (99%)
rename .github/workflows/{dependency-review.yml => analysis-dependency.yml} (97%)
rename .github/workflows/{scorecard.yml => analysis-scorecard.yml} (98%)
rename .github/workflows/{npm-publish-github-packages.yml => publish-github-packages.yml} (96%)
rename .github/workflows/{run-test.yml => test-ci.yml} (93%)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/analysis-codeql.yml
similarity index 99%
rename from .github/workflows/codeql.yml
rename to .github/workflows/analysis-codeql.yml
index 0f42cb1ff..b718e6878 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/analysis-codeql.yml
@@ -9,7 +9,7 @@
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
-name: "CodeQL"
+name: CodeQL Analysis
on:
push:
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/analysis-dependency.yml
similarity index 97%
rename from .github/workflows/dependency-review.yml
rename to .github/workflows/analysis-dependency.yml
index fe461b424..99afdf01c 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/analysis-dependency.yml
@@ -4,7 +4,7 @@
#
# Source repository: https://github.com/actions/dependency-review-action
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
-name: 'Dependency Review'
+name: Dependency Analysis
on: [pull_request]
permissions:
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/analysis-scorecard.yml
similarity index 98%
rename from .github/workflows/scorecard.yml
rename to .github/workflows/analysis-scorecard.yml
index 5844bc19a..0c8f2b902 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/analysis-scorecard.yml
@@ -2,7 +2,7 @@
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
-name: Scorecard supply-chain security
+name: OpenSSF's Scorecard Analysis
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
diff --git a/.github/workflows/npm-publish-github-packages.yml b/.github/workflows/publish-github-packages.yml
similarity index 96%
rename from .github/workflows/npm-publish-github-packages.yml
rename to .github/workflows/publish-github-packages.yml
index 47a37f98c..195c54547 100644
--- a/.github/workflows/npm-publish-github-packages.yml
+++ b/.github/workflows/publish-github-packages.yml
@@ -1,7 +1,7 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
-name: Node.js Package
+name: Publish GitHub Package
on:
release:
diff --git a/.github/workflows/run-test.yml b/.github/workflows/test-ci.yml
similarity index 93%
rename from .github/workflows/run-test.yml
rename to .github/workflows/test-ci.yml
index b3990c8f5..899ad44a8 100644
--- a/.github/workflows/run-test.yml
+++ b/.github/workflows/test-ci.yml
@@ -1,4 +1,4 @@
-name: ua-parser-js-run-test
+name: UAParser.js CI-Test
on: [push, pull_request]
From feefb81cd0241f9a85ca72319c9eea3edd86dbf5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 13 Apr 2023 00:20:02 +0000
Subject: [PATCH 089/388] Bump shelljs and jshint
Removes [shelljs](https://github.com/shelljs/shelljs). It's no longer used after updating ancestor dependency [jshint](https://github.com/jshint/jshint). These dependencies need to be updated together.
Removes `shelljs`
Updates `jshint` from 2.12.0 to 2.13.6
- [Release notes](https://github.com/jshint/jshint/releases)
- [Changelog](https://github.com/jshint/jshint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jshint/jshint/compare/2.12.0...2.13.6)
---
updated-dependencies:
- dependency-name: shelljs
dependency-type: indirect
- dependency-name: jshint
dependency-type: direct:development
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 23 +++++------------------
package.json | 2 +-
2 files changed, 6 insertions(+), 19 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index d696d4bcb..2e5b36611 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,7 +26,7 @@
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
"@playwright/test": "~1.32.2",
- "jshint": "~2.12.0",
+ "jshint": "~2.13.6",
"mocha": "~8.2.0",
"requirejs": "2.3.2",
"safe-regex": "^2.1.1",
@@ -928,18 +928,17 @@
}
},
"node_modules/jshint": {
- "version": "2.12.0",
- "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz",
- "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==",
+ "version": "2.13.6",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.6.tgz",
+ "integrity": "sha512-IVdB4G0NTTeQZrBoM8C5JFVLjV2KtZ9APgybDA1MK73xb09qFs0jCXyQLnCOp1cSZZZbvhq/6mfXHUTaDkffuQ==",
"dev": true,
"dependencies": {
"cli": "~1.0.0",
"console-browserify": "1.1.x",
"exit": "0.1.x",
"htmlparser2": "3.8.x",
- "lodash": "~4.17.19",
+ "lodash": "~4.17.21",
"minimatch": "~3.0.2",
- "shelljs": "0.3.x",
"strip-json-comments": "1.0.x"
},
"bin": {
@@ -1431,18 +1430,6 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true
},
- "node_modules/shelljs": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
- "integrity": "sha512-Ny0KN4dyT8ZSCE0frtcbAJGoM/HTArpyPkeli1/00aYfm0sbD/Gk/4x7N2DP9QKGpBsiQH7n6rpm1L79RtviEQ==",
- "dev": true,
- "bin": {
- "shjs": "bin/shjs"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
diff --git a/package.json b/package.json
index 451baa138..32c627af8 100644
--- a/package.json
+++ b/package.json
@@ -171,7 +171,7 @@
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
"@playwright/test": "~1.32.2",
- "jshint": "~2.12.0",
+ "jshint": "~2.13.6",
"mocha": "~8.2.0",
"requirejs": "2.3.2",
"safe-regex": "^2.1.1",
From 35c2b91534e47fd17b254f1bdfa016d22f21a820 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Fri, 14 Apr 2023 15:36:37 +0700
Subject: [PATCH 090/388] Fix: accept empty string as input
---
src/ua-parser.js | 23 ++++++++++-------------
test/playwright-test-browser.spec.mjs | 12 ++++++++++++
2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/src/ua-parser.js b/src/ua-parser.js
index d72fda7bb..5f306252a 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -918,10 +918,10 @@
// Constructor
////////////////
- function UACHData (uach, isHTTP_UACH) {
+ function UACHData (uach, isHttpUACH) {
uach = uach || {};
setProps.call(this, CH_ALL_VALUES);
- if (isHTTP_UACH) {
+ if (isHttpUACH) {
setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
@@ -1106,15 +1106,12 @@
return new UAParser(ua, extensions, headers).getResult();
}
- var userAgent = ua ||
- ((NAVIGATOR && NAVIGATOR.userAgent) ?
- NAVIGATOR.userAgent :
- (headers && headers[USER_AGENT] ?
- headers[USER_AGENT] :
- EMPTY)),
-
- HTTP_UACH = new UACHData(headers, true),
+ var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
+ ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
+ (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
+ EMPTY)), // empty string
+ httpUACH = new UACHData(headers, true),
regexMap = extensions ?
extend(defaultRegexes, extensions) :
defaultRegexes,
@@ -1122,7 +1119,7 @@
createItemFunc = function (itemType) {
if (itemType == UA_RESULT) {
return function () {
- return new UAItem(itemType, userAgent, regexMap, HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap, httpUACH)
.set('ua', userAgent)
.set(UA_BROWSER, this.getBrowser())
.set(UA_CPU, this.getCPU())
@@ -1133,13 +1130,13 @@
};
} else {
return function () {
- return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap[itemType], httpUACH)
.parseUA()
.get();
};
}
};
-
+
// public methods
setProps.call(this, [
['getBrowser', createItemFunc(UA_BROWSER)],
diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-browser.spec.mjs
index a3fb5b890..bab1f3a0b 100644
--- a/test/playwright-test-browser.spec.mjs
+++ b/test/playwright-test-browser.spec.mjs
@@ -5,6 +5,18 @@ import url from 'url';
const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../')}/dist/ua-parser.html`;
+test.describe('test input', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto(localHtml);
+ });
+
+ test('accept empty string', async ({ page }) => {
+ // @ts-ignore
+ const uap = await page.evaluate(async () => await UAParser(''));
+ expect(uap).toHaveProperty('ua', '');
+ });
+});
+
test('read client hints data', async ({ page }) => {
await page.addInitScript(() => {
Object.defineProperty(navigator, 'userAgentData', {
From 5a26ac146ee0c7afc3dd42d0cd93dbd14ceee4d6 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Fri, 14 Apr 2023 23:43:09 +0700
Subject: [PATCH 091/388] Create build+test scripts
---
.github/workflows/test-ci.yml | 3 +-
package.json | 6 +--
script/build-dist.sh | 13 ++++++
script/build-module.js | 75 ++++++++++++++++++++---------------
script/test-all.sh | 21 ++++++++++
5 files changed, 82 insertions(+), 36 deletions(-)
create mode 100755 script/build-dist.sh
mode change 100644 => 100755 script/build-module.js
create mode 100755 script/test-all.sh
diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml
index 899ad44a8..5c02b3dc5 100644
--- a/.github/workflows/test-ci.yml
+++ b/.github/workflows/test-ci.yml
@@ -19,6 +19,5 @@ jobs:
- name: Run the test
run: |
npm ci
- npm run build
npx playwright install
- npm run test-ci
+ npm test
diff --git a/package.json b/package.json
index 32c627af8..81e29058d 100644
--- a/package.json
+++ b/package.json
@@ -163,9 +163,8 @@
"src"
],
"scripts": {
- "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js",
- "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json",
- "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json"
+ "build": "./script/build-dist.sh && ./script/build-module.js",
+ "test": "npm run build && ./script/test-all.sh"
},
"devDependencies": {
"@babel/parser": "7.15.8",
@@ -187,6 +186,7 @@
},
"directories": {
"dist": "dist",
+ "script": "script",
"src": "src",
"test": "test"
},
diff --git a/script/build-dist.sh b/script/build-dist.sh
new file mode 100755
index 000000000..4df2da7e8
--- /dev/null
+++ b/script/build-dist.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+SRC_PATH="src/ua-parser.js"
+MIN_PATH="dist/ua-parser.min.js"
+PACK_PATH="dist/ua-parser.pack.js"
+
+# minified
+echo "Generate ${MIN_PATH}"
+uglifyjs $SRC_PATH -o $MIN_PATH --comments "/^ UA/"
+
+# packed
+echo "Generate ${PACK_PATH}"
+uglifyjs $SRC_PATH -o $PACK_PATH --comments "/^ UA/" --compress --mangle
\ No newline at end of file
diff --git a/script/build-module.js b/script/build-module.js
old mode 100644
new mode 100755
index 5b47349dc..4d36273e7
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -1,40 +1,53 @@
#!/usr/bin/env node
+/* jshint esversion: 6 */
const fs = require('fs');
-
-/*///////////////
-// ua-parser.mjs
-//////////////*/
-
-fs.writeFileSync('src/module/ua-parser.mjs',
-`// Generated ESM version of UAParser.js
+const PATH = {
+ main : {
+ src : 'src/ua-parser.js',
+ dest : 'src/module/ua-parser.mjs',
+ title : ''
+ },
+ enum : {
+ src : 'src/ua-parser-enum.js',
+ dest :'src/module/ua-parser-enum.mjs',
+ title : 'enum'
+ },
+ extension : {
+ src : 'src/ua-parser-extension.js',
+ dest : 'src/module/ua-parser-extension.mjs',
+ title : 'extension'
+ }
+};
+const generateMJS = (module, replacers) => {
+ const { src, dest, title } = PATH[module];
+ let text = fs.readFileSync(src, 'utf-8');
+ replacers.forEach(replacer => {
+ text = text.replace(replacer[0], replacer[1]);
+ });
+
+ console.log(`Generate ${dest}`);
+
+ fs.writeFileSync(dest,
+`// Generated ESM version of UAParser.js ${title}
// DO NOT EDIT THIS FILE!
// Source: /src/ua-parser.js
-` + fs.readFileSync('src/ua-parser.js','utf-8').replace(/\(func[\s\S]+strict\';/ig,'')
- .replace(/esversion\: 3/ig, 'esversion: 6')
- .replace(/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'),'utf-8');
-
-/*/////////////////////
-// ua-parser-enum.mjs
-////////////////////*/
-
-fs.writeFileSync('src/module/ua-parser-enum.mjs',
-`// Generated ESM version of UAParser.js enums
-// DO NOT EDIT THIS FILE!
-// Source: /src/ua-parser-enum.js
+${text}`, 'utf-8');
+};
-` + fs.readFileSync('src/ua-parser-enum.js','utf-8')
- .replace(/module\.exports =/ig,'export'),'utf-8');
+if (!fs.existsSync('src/module')) {
+ fs.mkdirSync('src/module', { recursive: true });
+}
-/*//////////////////////////
-// ua-parser-extension.mjs
-/////////////////////////*/
+// ua-parser.mjs
+generateMJS('main', [
+ [/\(func[\s\S]+strict\';/ig, ''],
+ [/esversion\: 3/ig, 'esversion: 6'],
+ [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};']
+]);
-fs.writeFileSync('src/module/ua-parser-extension.mjs',
-`// Generated ESM version of UAParser.js extensions
-// DO NOT EDIT THIS FILE!
-// Source: /src/ua-parser-extension.js
+// ua-parser-enum.mjs
+generateMJS('enum', [[/module\.exports =/ig, 'export']]);
-` + fs.readFileSync('src/ua-parser-extension.js','utf-8')
- .replace(/const UA.+\)/ig,'import UAParser from \'ua-parser-js\'')
- .replace(/module\.exports =/ig,'export'),'utf-8');
\ No newline at end of file
+// ua-parser-extension.mjs
+generateMJS('extension', [[/module\.exports =/ig, 'export']]);
\ No newline at end of file
diff --git a/script/test-all.sh b/script/test-all.sh
new file mode 100755
index 000000000..b77df1a85
--- /dev/null
+++ b/script/test-all.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+echo '
+- lint js code
+'
+jshint src && jshint script || exit 1
+
+echo '
+- test using mocha
+'
+mocha -R list test/mocha*js || exit 1
+
+echo '
+- test using playwright
+'
+npx playwright test || exit 1
+
+echo '
+- lint lockfile
+'
+npx lockfile-lint -p package-lock.json || exit 1
\ No newline at end of file
From c3be7326b86dec9f705fe0264a0b652e2c3b1ef6 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 15 Apr 2023 00:01:15 +0700
Subject: [PATCH 092/388] Update GitHub package action - change trigger to
'published'
---
.github/workflows/publish-github-packages.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml
index 195c54547..eaea41c37 100644
--- a/.github/workflows/publish-github-packages.yml
+++ b/.github/workflows/publish-github-packages.yml
@@ -5,7 +5,7 @@ name: Publish GitHub Package
on:
release:
- types: [created]
+ types: [published]
jobs:
build:
From 29fb85658adb66e4b842ffe721b38e77f4ec5cd1 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 15 Apr 2023 01:08:46 +0700
Subject: [PATCH 093/388] Fix #643 - Improve iOS detection + detect Slack
(&Slackbot)
---
src/ua-parser-extension.js | 20 +++++++++++++++-----
src/ua-parser.js | 2 +-
test/mocha-test-extension.js | 12 ++++++------
test/specs/os-all.json | 9 +++++++++
4 files changed, 31 insertions(+), 12 deletions(-)
diff --git a/src/ua-parser-extension.js b/src/ua-parser-extension.js
index fed987eb3..329129ebd 100644
--- a/src/ua-parser-extension.js
+++ b/src/ua-parser-extension.js
@@ -15,10 +15,19 @@ 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
- [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
+ // Googlebot / BingBot / MSNBot / FacebookBot
+ [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
+
+ // Slackbot - https://api.slack.com/robots
+ [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
]
});
@@ -107,16 +116,17 @@ const Emails = Object.freeze({
]
});
-const Tools = Object.freeze({
+const CLI = Object.freeze({
browser : [
// wget / curl / lynx
- [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']]
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
]
});
module.exports = {
+ Apps,
Bots,
ExtraDevices,
Emails,
- Tools
+ CLI
};
\ No newline at end of file
diff --git a/src/ua-parser.js b/src/ua-parser.js
index 5f306252a..13d5507c8 100755
--- a/src/ua-parser.js
+++ b/src/ua-parser.js
@@ -732,7 +732,7 @@
// iOS/macOS
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
- /ios;fbsv\/([\d\.]+)/i,
+ /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
/cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i,
diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js
index 250ee6442..c97c16d26 100644
--- a/test/mocha-test-extension.js
+++ b/test/mocha-test-extension.js
@@ -1,7 +1,7 @@
const assert = require('assert');
const safeRegex = require('safe-regex');
const UAParser = require('ua-parser-js');
-const { Bots, Emails, Tools } = require('ua-parser-js/extensions');
+const { Bots, Emails, CLI } = require('ua-parser-js/extensions');
describe('Bots', () => {
it('Can detect bots', () => {
@@ -20,11 +20,11 @@ describe('Bots', () => {
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"});
- // try merging Bots & Tools
- const botsAndTools = { browser : [...Bots.browser, ...Tools.browser]};
- const botolParser = new UAParser(botsAndTools);
- assert.deepEqual(botolParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"tool"});
- assert.deepEqual(botolParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"});
+ // try merging Bots & CLIs
+ const botsAndCLIs = { browser : [...Bots.browser, ...CLI.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"});
const emailParser = new UAParser(Emails);
assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"});
diff --git a/test/specs/os-all.json b/test/specs/os-all.json
index 3700e5339..5bf7630b0 100644
--- a/test/specs/os-all.json
+++ b/test/specs/os-all.json
@@ -773,6 +773,15 @@
"version" : "13.6.1"
}
},
+ {
+ "desc": "iOS with Slack App",
+ "ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)",
+ "expect":
+ {
+ "name" : "iOS",
+ "version" : "16.4.1"
+ }
+ },
{
"desc" : "watchOS",
"ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]",
From 1653d376ca61c7082ba6d0f22105f97ad931dfe6 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 15 Apr 2023 13:45:31 +0700
Subject: [PATCH 094/388] Update issue templates
---
.github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++
.github/ISSUE_TEMPLATE/custom.md | 10 ++++++
.github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++
3 files changed, 68 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
create mode 100644 .github/ISSUE_TEMPLATE/custom.md
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..dd84ea782
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md
new file mode 100644
index 000000000..48d5f81fa
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/custom.md
@@ -0,0 +1,10 @@
+---
+name: Custom issue template
+about: Describe this issue template's purpose here.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..bbcbbe7d6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
From 9102871dea6d877fc8f40b150e074694a56b13f4 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Fri, 21 Apr 2023 09:47:51 +0700
Subject: [PATCH 095/388] Rearrange the structure of src folders
---
package.json | 17 ++++++-----
script/build-dist.sh | 2 +-
script/build-module.js | 30 ++++++++-----------
.../ua-parser-enums.js} | 0
.../ua-parser-enums.mjs} | 2 +-
.../ua-parser-extensions.js} | 0
.../ua-parser-extensions.mjs} | 22 ++++++++++----
src/{ => main}/ua-parser.js | 0
src/{module => main}/ua-parser.mjs | 29 ++++++++----------
test/mocha-test.js | 4 +--
10 files changed, 55 insertions(+), 51 deletions(-)
rename src/{ua-parser-enum.js => enums/ua-parser-enums.js} (100%)
rename src/{module/ua-parser-enum.mjs => enums/ua-parser-enums.mjs} (98%)
rename src/{ua-parser-extension.js => extensions/ua-parser-extensions.js} (100%)
rename src/{module/ua-parser-extension.mjs => extensions/ua-parser-extensions.mjs} (91%)
rename src/{ => main}/ua-parser.js (100%)
rename src/{module => main}/ua-parser.mjs (98%)
diff --git a/package.json b/package.json
index 81e29058d..dbe33f8d9 100644
--- a/package.json
+++ b/package.json
@@ -142,20 +142,21 @@
"Zach Bjornson "
],
"type": "commonjs",
- "main": "src/ua-parser.js",
- "module": "src/module/ua-parser.mjs",
+ "main": "src/main/ua-parser.js",
+ "module": "src/main/ua-parser.mjs",
+ "browser": "dist/ua-parser.pack.js",
"exports": {
".": {
- "require": "./src/ua-parser.js",
- "import": "./src/module/ua-parser.mjs"
+ "require": "./src/main/ua-parser.js",
+ "import": "./src/main/ua-parser.mjs"
},
"./enums": {
- "require": "./src/ua-parser-enum.js",
- "import": "./src/module/ua-parser-enum.mjs"
+ "require": "./src/enums/ua-parser-enums.js",
+ "import": "./src/enums/ua-parser-enums.mjs"
},
"./extensions": {
- "require": "./src/ua-parser-extension.js",
- "import": "./src/module/ua-parser-extension.mjs"
+ "require": "./src/extensions/ua-parser-extensions.js",
+ "import": "./src/extensions/ua-parser-extensions.mjs"
}
},
"files": [
diff --git a/script/build-dist.sh b/script/build-dist.sh
index 4df2da7e8..884f4b423 100755
--- a/script/build-dist.sh
+++ b/script/build-dist.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-SRC_PATH="src/ua-parser.js"
+SRC_PATH="src/main/ua-parser.js"
MIN_PATH="dist/ua-parser.min.js"
PACK_PATH="dist/ua-parser.pack.js"
diff --git a/script/build-module.js b/script/build-module.js
index 4d36273e7..bbeb72cc3 100755
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -3,19 +3,19 @@
const fs = require('fs');
const PATH = {
main : {
- src : 'src/ua-parser.js',
- dest : 'src/module/ua-parser.mjs',
+ src : 'src/main/ua-parser.js',
+ dest : 'src/main/ua-parser.mjs',
title : ''
},
- enum : {
- src : 'src/ua-parser-enum.js',
- dest :'src/module/ua-parser-enum.mjs',
- title : 'enum'
+ enums : {
+ src : 'src/enums/ua-parser-enums.js',
+ dest :'src/enums/ua-parser-enums.mjs',
+ title : 'enums'
},
- extension : {
- src : 'src/ua-parser-extension.js',
- dest : 'src/module/ua-parser-extension.mjs',
- title : 'extension'
+ extensions : {
+ src : 'src/extensions/ua-parser-extensions.js',
+ dest : 'src/extensions/ua-parser-extensions.mjs',
+ title : 'extensions'
}
};
const generateMJS = (module, replacers) => {
@@ -30,15 +30,11 @@ const generateMJS = (module, replacers) => {
fs.writeFileSync(dest,
`// Generated ESM version of UAParser.js ${title}
// DO NOT EDIT THIS FILE!
-// Source: /src/ua-parser.js
+// Source: /${src}
${text}`, 'utf-8');
};
-if (!fs.existsSync('src/module')) {
- fs.mkdirSync('src/module', { recursive: true });
-}
-
// ua-parser.mjs
generateMJS('main', [
[/\(func[\s\S]+strict\';/ig, ''],
@@ -47,7 +43,7 @@ generateMJS('main', [
]);
// ua-parser-enum.mjs
-generateMJS('enum', [[/module\.exports =/ig, 'export']]);
+generateMJS('enums', [[/module\.exports =/ig, 'export']]);
// ua-parser-extension.mjs
-generateMJS('extension', [[/module\.exports =/ig, 'export']]);
\ No newline at end of file
+generateMJS('extensions', [[/module\.exports =/ig, 'export']]);
\ No newline at end of file
diff --git a/src/ua-parser-enum.js b/src/enums/ua-parser-enums.js
similarity index 100%
rename from src/ua-parser-enum.js
rename to src/enums/ua-parser-enums.js
diff --git a/src/module/ua-parser-enum.mjs b/src/enums/ua-parser-enums.mjs
similarity index 98%
rename from src/module/ua-parser-enum.mjs
rename to src/enums/ua-parser-enums.mjs
index 2081f10f5..d766e247f 100644
--- a/src/module/ua-parser-enum.mjs
+++ b/src/enums/ua-parser-enums.mjs
@@ -1,6 +1,6 @@
// Generated ESM version of UAParser.js enums
// DO NOT EDIT THIS FILE!
-// Source: /src/ua-parser-enum.js
+// Source: /src/enums/ua-parser-enums.js
///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.0-alpha.2
diff --git a/src/ua-parser-extension.js b/src/extensions/ua-parser-extensions.js
similarity index 100%
rename from src/ua-parser-extension.js
rename to src/extensions/ua-parser-extensions.js
diff --git a/src/module/ua-parser-extension.mjs b/src/extensions/ua-parser-extensions.mjs
similarity index 91%
rename from src/module/ua-parser-extension.mjs
rename to src/extensions/ua-parser-extensions.mjs
index 44932a2c9..d0b6f8cdc 100644
--- a/src/module/ua-parser-extension.mjs
+++ b/src/extensions/ua-parser-extensions.mjs
@@ -1,6 +1,6 @@
// Generated ESM version of UAParser.js extensions
// DO NOT EDIT THIS FILE!
-// Source: /src/ua-parser-extension.js
+// Source: /src/extensions/ua-parser-extensions.js
///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.0-alpha.2
@@ -19,10 +19,19 @@ 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
- [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
+ // Googlebot / BingBot / MSNBot / FacebookBot
+ [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
+
+ // Slackbot - https://api.slack.com/robots
+ [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
]
});
@@ -111,16 +120,17 @@ const Emails = Object.freeze({
]
});
-const Tools = Object.freeze({
+const CLI = Object.freeze({
browser : [
// wget / curl / lynx
- [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']]
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
]
});
export {
+ Apps,
Bots,
ExtraDevices,
Emails,
- Tools
+ CLI
};
\ No newline at end of file
diff --git a/src/ua-parser.js b/src/main/ua-parser.js
similarity index 100%
rename from src/ua-parser.js
rename to src/main/ua-parser.js
diff --git a/src/module/ua-parser.mjs b/src/main/ua-parser.mjs
similarity index 98%
rename from src/module/ua-parser.mjs
rename to src/main/ua-parser.mjs
index 250eea72a..e49eb7278 100644
--- a/src/module/ua-parser.mjs
+++ b/src/main/ua-parser.mjs
@@ -1,6 +1,6 @@
-// Generated ESM version of UAParser.js
+// Generated ESM version of UAParser.js
// DO NOT EDIT THIS FILE!
-// Source: /src/ua-parser.js
+// Source: /src/main/ua-parser.js
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.0-alpha.2
@@ -734,7 +734,7 @@
// iOS/macOS
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
- /ios;fbsv\/([\d\.]+)/i,
+ /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
/cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i,
@@ -920,10 +920,10 @@
// Constructor
////////////////
- function UACHData (uach, isHTTP_UACH) {
+ function UACHData (uach, isHttpUACH) {
uach = uach || {};
setProps.call(this, CH_ALL_VALUES);
- if (isHTTP_UACH) {
+ if (isHttpUACH) {
setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
@@ -1108,15 +1108,12 @@
return new UAParser(ua, extensions, headers).getResult();
}
- var userAgent = ua ||
- ((NAVIGATOR && NAVIGATOR.userAgent) ?
- NAVIGATOR.userAgent :
- (headers && headers[USER_AGENT] ?
- headers[USER_AGENT] :
- EMPTY)),
-
- HTTP_UACH = new UACHData(headers, true),
+ var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
+ ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
+ (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
+ EMPTY)), // empty string
+ httpUACH = new UACHData(headers, true),
regexMap = extensions ?
extend(defaultRegexes, extensions) :
defaultRegexes,
@@ -1124,7 +1121,7 @@
createItemFunc = function (itemType) {
if (itemType == UA_RESULT) {
return function () {
- return new UAItem(itemType, userAgent, regexMap, HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap, httpUACH)
.set('ua', userAgent)
.set(UA_BROWSER, this.getBrowser())
.set(UA_CPU, this.getCPU())
@@ -1135,13 +1132,13 @@
};
} else {
return function () {
- return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH)
+ return new UAItem(itemType, userAgent, regexMap[itemType], httpUACH)
.parseUA()
.get();
};
}
};
-
+
// public methods
setProps.call(this, [
['getBrowser', createItemFunc(UA_BROWSER)],
diff --git a/test/mocha-test.js b/test/mocha-test.js
index e85322dd6..682f710e0 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('../src/ua-parser');
+var UAParser = require('ua-parser-js');
var browsers = require('./specs/browser-all.json');
var cpus = require('./specs/cpu-all.json');
var devices = require('./specs/device-all.json');
@@ -159,7 +159,7 @@ describe('Testing regexes', function () {
var regexes;
before('Read main js file', function () {
- var code = fs.readFileSync('src/ua-parser.js', 'utf8').toString();
+ var code = fs.readFileSync('src/main/ua-parser.js', 'utf8').toString();
var ast = parseJS(code, { sourceType: "script" });
regexes = [];
traverse(ast, {
From 4c77c5ef2176401ad02fdfe05b8cd6f4653af7dc Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 24 Apr 2023 13:49:47 +0700
Subject: [PATCH 096/388] Revive the extensive list of MediaPlayers regexes by
@leofiore as an Extension
(Original commit reference: 3fa1fe9f704d716e8f57dea46bccc5724da7b952)
---
src/extensions/ua-parser-extensions.js | 225 ++++++++++++++++++-------
test/mocha-test-extension.js | 45 ++++-
2 files changed, 204 insertions(+), 66 deletions(-)
diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js
index 329129ebd..30de6d1b0 100644
--- a/src/extensions/ua-parser-extensions.js
+++ b/src/extensions/ua-parser-extensions.js
@@ -24,109 +24,214 @@ const Apps = Object.freeze({
const Bots = Object.freeze({
browser : [
// Googlebot / BingBot / MSNBot / FacebookBot
- [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
+ [/((?:google|bing|msn|facebook)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']]
]
});
+const CLIs = Object.freeze({
+ browser : [
+ // wget / curl / lynx
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
+ ]
+});
+
const ExtraDevices = Object.freeze({
- device : [
- [
- /(nook)[\w ]+build\/(\w+)/i, // Nook
- /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
- /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
- /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
- /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
- /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
+ device : [[
+ /(nook)[\w ]+build\/(\w+)/i, // Nook
+ /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
+ /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
+ /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
+ /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
+ /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
+ ], [VENDOR, MODEL, [TYPE, TABLET]], [
- /(u304aa)/i // AT&T
- ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
+ /(u304aa)/i // AT&T
+ ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
- /\bsie-(\w*)/i // Siemens
- ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
+ /\bsie-(\w*)/i // Siemens
+ ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
- /\b(rct\w+) b/i // RCA Tablets
- ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
+ /\b(rct\w+) b/i // RCA Tablets
+ ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
- /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
- ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
+ /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
+ ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
- /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
- ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
+ /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
+ ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
- /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
- ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
+ /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
+ ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
- /\b(tm\d{3}\w+) b/i
- ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
+ /\b(tm\d{3}\w+) b/i
+ ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
- /\b(k88) b/i // ZTE K Series Tablet
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
+ /\b(k88) b/i // ZTE K Series Tablet
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
- /\b(nx\d{3}j) b/i // ZTE Nubia
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
+ /\b(nx\d{3}j) b/i // ZTE Nubia
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
- /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
+ /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
- /\b(zur\d{3}) b/i // Swiss ZUR Tablet
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
+ /\b(zur\d{3}) b/i // Swiss ZUR Tablet
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
- /\b((zeki)?tb.*\b) b/i // Zeki Tablets
- ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
+ /\b((zeki)?tb.*\b) b/i // Zeki Tablets
+ ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
- /\b([yr]\d{2}) b/i,
- /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
- ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [
+ /\b([yr]\d{2}) b/i,
+ /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
+ ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [
- /\b(ns-?\w{0,9}) b/i // Insignia Tablets
- ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
+ /\b(ns-?\w{0,9}) b/i // Insignia Tablets
+ ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
- /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
- ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
+ /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
+ ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
- /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
- ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
+ /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
+ ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
- /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
- ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
+ /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
+ ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
- /\b(ph-1) /i // Essential PH-1
- ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
+ /\b(ph-1) /i // Essential PH-1
+ ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
- /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
- ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
+ /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
+ ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
- /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
- ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
+ /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
+ ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
- /\btu_(1491) b/i // Rotor Tablets
- ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]
- ]
+ /\btu_(1491) b/i // Rotor Tablets
+ ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]]
]
});
const Emails = Object.freeze({
browser : [
- // Microsoft Outlook / Thunderbird
+ // Microsoft Outlook / Thunderbird
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
]
});
-const CLI = Object.freeze({
- browser : [
- // wget / curl / lynx
- [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
+const MediaPlayers = Object.freeze({
+ browser : [[
+
+ /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia
+ /(coremedia) v([\w\._]+)/i
+ ], [NAME, VERSION], [
+
+ /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer
+ ], [NAME, VERSION], [
+
+ /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy
+ ], [NAME, VERSION], [
+
+ /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i,
+ // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC
+ // NSPlayer/PSP-InternetRadioPlayer/Videos
+ /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD
+ /(lg player|nexplayer)\s([\d\.]+)/i,
+ /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player
+ ], [NAME, VERSION], [
+ /(nexplayer)\s([\w\.-]+)/i // Nexplayer
+ ], [NAME, VERSION], [
+
+ /(flrp)\/([\w\.-]+)/i // Flip Player
+ ], [[NAME, 'Flip Player'], VERSION], [
+
+ /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i
+ // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit
+ ], [NAME], [
+
+ /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i
+ // Gstreamer
+ ], [NAME, VERSION], [
+
+ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player
+ /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i,
+ // Java/urllib/requests/wget/cURL
+ /(lavf)([\d\.]+)/i // Lavf (FFMPEG)
+ ], [NAME, VERSION], [
+
+ /(htc_one_s)\/([\d\.]+)/i, // HTC One S
+ ], [[NAME, /_/g, ' '], VERSION], [
+
+ /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i,
+ // MPlayer SVN
+ ], [NAME, VERSION], [
+
+ /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer
+ /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN
+ ], [NAME, VERSION], [
+
+ /(mplayer)/i, // MPlayer (no other info)
+ /(yourmuze)/i, // YourMuze
+ /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime
+ ], [NAME], [
+
+ /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout
+ ], [NAME, VERSION], [
+
+ /(nokia\d+)\/([\w\.-]+)/i // Nokia
+ ], [NAME, VERSION], [
+
+ /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird
+ ], [NAME, VERSION], [
+
+ /(winamp)3 version ([\w\.-]+)/i, // Winamp
+ /(winamp)\s([\w\.-]+)/i,
+ /(winamp)mpeg\/([\w\.-]+)/i
+ ], [NAME, VERSION], [
+
+ /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info)
+ // inlight radio
+ ], [NAME], [
+
+ /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i
+ // QuickTime/RealMedia/RadioApp/RadioClientApplication/
+ // SoundTap/Totem/Stagefright/Streamium
+ ], [NAME, VERSION], [
+
+ /(smp)([\d\.]+)/i // SMP
+ ], [NAME, VERSION], [
+
+ /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan
+ /(vlc)\/([\w\.-]+)/i,
+ /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp
+ /(foobar2000)\/([\d\.]+)/i, // Foobar2000
+ /(itunes)\/([\d\.]+)/i // iTunes
+ ], [NAME, VERSION], [
+
+ /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player
+ /(windows-media-player)\/([\w\.-]+)/i
+ ], [[NAME, /-/g, ' '], VERSION], [
+
+ /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i,
+ // Windows Media Server
+ ], [VERSION, [NAME, 'Windows']], [
+
+ /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm
+ ], [NAME, VERSION], [
+
+ /(rad.io)\s([\d\.]+)/i, // Rad.io
+ /(radio.(?:de|at|fr))\s([\d\.]+)/i
+ ], [[NAME, 'rad.io'], VERSION]
]
});
module.exports = {
Apps,
Bots,
+ CLIs,
ExtraDevices,
Emails,
- CLI
+ MediaPlayers
};
\ No newline at end of file
diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js
index c97c16d26..451a99c0a 100644
--- a/test/mocha-test-extension.js
+++ b/test/mocha-test-extension.js
@@ -1,7 +1,10 @@
+const fs = require('fs');
const assert = require('assert');
-const safeRegex = require('safe-regex');
+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, Emails, CLI } = require('ua-parser-js/extensions');
+const Ext = require('ua-parser-js/extensions');
describe('Bots', () => {
it('Can detect bots', () => {
@@ -14,23 +17,53 @@ describe('Bots', () => {
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 botParser = new UAParser(Bots);
+ const botParser = new UAParser(Ext.Bots);
assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", 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"});
// try merging Bots & CLIs
- const botsAndCLIs = { browser : [...Bots.browser, ...CLI.browser]};
+ const botsAndCLIs = { browser : [...Ext.Bots.browser, ...Ext.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"});
- const emailParser = new UAParser(Emails);
+ const emailParser = new UAParser(Ext.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"});
});
});
// TODO : move test spec to JSON file
-// TODO : check for safe-regex
\ No newline at end of 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);
+ });
+ });
+ });
+ });
+ });
+});
\ No newline at end of file
From a74ebeb82e8cf39e3c96fd53308d2bfcac745db7 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 26 Apr 2023 13:53:29 +0700
Subject: [PATCH 097/388] Only allow string for setUA()
---
src/main/ua-parser.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index 13d5507c8..65bab0292 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -1147,7 +1147,8 @@
['getResult', createItemFunc(UA_RESULT)],
['getUA', function () { return userAgent; }],
['setUA', function (ua) {
- userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
+ if (typeof ua === STR_TYPE)
+ userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
return this;
}]
])
From 3d5c70457e588a5ab1c38d2586d55b697a2a4e8a Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 27 Apr 2023 07:04:51 +0700
Subject: [PATCH 098/388] Fuzz testing using Jazzer.js
---
package-lock.json | 2335 ++++++++++++++++++++++++++++++++---
package.json | 2 +
test/jazzer-test-fuzzing.js | 15 +
3 files changed, 2172 insertions(+), 180 deletions(-)
create mode 100644 test/jazzer-test-fuzzing.js
diff --git a/package-lock.json b/package-lock.json
index 2e5b36611..694dedaa2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
+ "@jazzer.js/core": "^1.4.0",
"@playwright/test": "~1.32.2",
"jshint": "~2.13.6",
"mocha": "~8.2.0",
@@ -36,6 +37,19 @@
"node": "*"
}
},
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.21.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
@@ -48,6 +62,78 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/compat-data": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz",
+ "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
+ "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-compilation-targets": "^7.21.4",
+ "@babel/helper-module-transforms": "^7.21.2",
+ "@babel/helpers": "^7.21.0",
+ "@babel/parser": "^7.21.4",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.2",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/@babel/parser": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/core/node_modules/@babel/traverse": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+ "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.21.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/generator": {
"version": "7.21.4",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
@@ -63,6 +149,34 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz",
+ "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.21.4",
+ "@babel/helper-validator-option": "^7.21.0",
+ "browserslist": "^4.21.3",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-function-name": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
@@ -88,6 +202,82 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
+ "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.21.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.21.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
+ "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.2",
+ "@babel/types": "^7.21.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+ "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.21.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+ "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-split-export-declaration": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
@@ -118,6 +308,62 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
+ "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
+ "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.0",
+ "@babel/types": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers/node_modules/@babel/parser": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/helpers/node_modules/@babel/traverse": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+ "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.21.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/highlight": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
@@ -204,119 +450,350 @@
"node": ">=6.9.0"
}
},
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
- "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jazzer.js/core": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@jazzer.js/core/-/core-1.4.0.tgz",
+ "integrity": "sha512-Wy2O6bWTOtYpfaSskYUq+iWtE6zIfp2Kang+2FvT+KT6lNMhoen/tCYZqUcMdDLgKpEqIZiDoAvMej8LgZZIxg==",
"dev": true,
"dependencies": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
+ "@jazzer.js/hooking": "*",
+ "@jazzer.js/instrumentor": "*",
+ "istanbul-lib-coverage": "^3.2.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-reports": "^3.1.5",
+ "tmp": "^0.2.1",
+ "yargs": "^17.7.1"
+ },
+ "bin": {
+ "jazzer": "dist/cli.js"
},
"engines": {
- "node": ">=6.0.0"
+ "node": ">= 14.0.0",
+ "npm": ">= 7.0.0"
}
},
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
- "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "node_modules/@jazzer.js/core/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
- "node": ">=6.0.0"
+ "node": ">=8"
}
},
- "node_modules/@jridgewell/set-array": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
- "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "node_modules/@jazzer.js/core/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
"engines": {
- "node": ">=6.0.0"
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.15",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.18",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
- "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+ "node_modules/@jazzer.js/core/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"dependencies": {
- "@jridgewell/resolve-uri": "3.1.0",
- "@jridgewell/sourcemap-codec": "1.4.14"
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
- "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
- "dev": true
- },
- "node_modules/@playwright/test": {
- "version": "1.32.3",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz",
- "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==",
+ "node_modules/@jazzer.js/core/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
- "@types/node": "*",
- "playwright-core": "1.32.3"
- },
- "bin": {
- "playwright": "cli.js"
+ "color-name": "~1.1.4"
},
"engines": {
- "node": ">=14"
- },
- "optionalDependencies": {
- "fsevents": "2.3.2"
+ "node": ">=7.0.0"
}
},
- "node_modules/@types/node": {
- "version": "18.15.11",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
- "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
+ "node_modules/@jazzer.js/core/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "node_modules/@ungap/promise-all-settled": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
- "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+ "node_modules/@jazzer.js/core/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
- "node_modules/ansi-colors": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
- "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "node_modules/@jazzer.js/core/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
- "node": ">=6"
+ "node": ">=8"
}
},
- "node_modules/ansi-regex": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
- "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "node_modules/@jazzer.js/core/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
"engines": {
- "node": ">=4"
+ "node": ">=8"
}
},
- "node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "node_modules/@jazzer.js/core/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
- "color-convert": "^1.9.0"
+ "ansi-regex": "^5.0.1"
},
"engines": {
- "node": ">=4"
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jazzer.js/core/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@jazzer.js/core/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@jazzer.js/core/node_modules/yargs": {
+ "version": "17.7.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+ "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jazzer.js/core/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jazzer.js/fuzzer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@jazzer.js/fuzzer/-/fuzzer-1.4.0.tgz",
+ "integrity": "sha512-GSmoMZ1KCSOJoSoxrLQRELBzQcvmfJ6+CiFWbKiSFdYc6MGr3yIpROZu77vnGUSVr8hJnDA6vffbcM9dOk5r+g==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "cmake-js": "^7.2.1",
+ "node-addon-api": "^6.0.0",
+ "prebuild-install": "^7.1.1"
+ },
+ "engines": {
+ "node": ">= 14.0.0",
+ "npm": ">= 7.0.0"
+ }
+ },
+ "node_modules/@jazzer.js/hooking": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@jazzer.js/hooking/-/hooking-1.4.0.tgz",
+ "integrity": "sha512-HubGSnLv8wfEoj81O0dxqyaLk69QGUvDGdRkZ4QydKmVvph70lLuH3DC4O7xHdneoA5Cv37qRHGvl5whkAEYnA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.21.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0",
+ "npm": ">= 7.0.0"
+ }
+ },
+ "node_modules/@jazzer.js/instrumentor": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@jazzer.js/instrumentor/-/instrumentor-1.4.0.tgz",
+ "integrity": "sha512-DkgalciiNIPzQaVsNM1FzzdKwnMcFDaJvB8AL+byKtdz3wEj4XoGSoMDk3tJmendP59FJHaOBxSxAjPBURv2ug==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.21.0",
+ "@babel/generator": "^7.21.1",
+ "@jazzer.js/fuzzer": "*",
+ "@jazzer.js/hooking": "*",
+ "istanbul-lib-hook": "^3.0.0",
+ "istanbul-lib-instrument": "^5.2.1",
+ "proper-lockfile": "^4.1.2",
+ "source-map-support": "^0.5.21"
+ },
+ "engines": {
+ "node": ">= 14.0.0",
+ "npm": ">= 7.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.18",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.32.3",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz",
+ "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.32.3"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.15.11",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
+ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
+ "dev": true
+ },
+ "node_modules/@ungap/promise-all-settled": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
+ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+ "dev": true
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
}
},
"node_modules/anymatch": {
@@ -332,6 +809,60 @@
"node": ">= 8"
}
},
+ "node_modules/append-transform": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz",
+ "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
+ "dev": true,
+ "dependencies": {
+ "default-require-extensions": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/aproba": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+ "dev": true
+ },
+ "node_modules/are-we-there-yet": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
+ "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
+ "dev": true,
+ "dependencies": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/are-we-there-yet/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/are-we-there-yet/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -341,12 +872,49 @@
"sprintf-js": "~1.0.2"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true
+ },
+ "node_modules/axios": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz",
+ "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==",
+ "dev": true,
+ "dependencies": {
+ "follow-redirects": "^1.15.0",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -356,6 +924,49 @@
"node": ">=8"
}
},
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/bl/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -384,6 +995,64 @@
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
+ "node_modules/browserslist": {
+ "version": "4.21.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
+ "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001449",
+ "electron-to-chromium": "^1.4.284",
+ "node-releases": "^2.0.8",
+ "update-browserslist-db": "^1.0.10"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -393,6 +1062,26 @@
"node": ">=6"
}
},
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001481",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz",
+ "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -413,78 +1102,297 @@
"integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
"dev": true,
"dependencies": {
- "anymatch": "~3.1.1",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.0",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.5.0"
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.5.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.1.2"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==",
+ "dev": true,
+ "dependencies": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=0.2.5"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cmake-js": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.2.1.tgz",
+ "integrity": "sha512-AdPSz9cSIJWdKvm0aJgVu3X8i0U3mNTswJkSHzZISqmYVjZk7Td4oDFg0mCBA383wO+9pG5Ix7pEP1CZH9x2BA==",
+ "dev": true,
+ "dependencies": {
+ "axios": "^1.3.2",
+ "debug": "^4",
+ "fs-extra": "^10.1.0",
+ "lodash.isplainobject": "^4.0.6",
+ "memory-stream": "^1.0.0",
+ "node-api-headers": "^0.0.2",
+ "npmlog": "^6.0.2",
+ "rc": "^1.2.7",
+ "semver": "^7.3.8",
+ "tar": "^6.1.11",
+ "url-join": "^4.0.1",
+ "which": "^2.0.2",
+ "yargs": "^17.6.0"
+ },
+ "bin": {
+ "cmake-js": "bin/cmake-js"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ }
+ },
+ "node_modules/cmake-js/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cmake-js/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cmake-js/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cmake-js/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/cmake-js/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/cmake-js/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/cmake-js/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cmake-js/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cmake-js/node_modules/semver": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
+ "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cmake-js/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
},
"engines": {
- "node": ">= 8.10.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.1.2"
+ "node": ">=8"
}
},
- "node_modules/cli": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
- "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==",
+ "node_modules/cmake-js/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
- "exit": "0.1.2",
- "glob": "^7.1.1"
+ "ansi-regex": "^5.0.1"
},
"engines": {
- "node": ">=0.2.5"
+ "node": ">=8"
}
},
- "node_modules/cliui": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
- "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "node_modules/cmake-js/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"dependencies": {
- "string-width": "^3.1.0",
- "strip-ansi": "^5.2.0",
- "wrap-ansi": "^5.1.0"
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "node_modules/cliui/node_modules/ansi-regex": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "node_modules/cmake-js/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"engines": {
- "node": ">=6"
+ "node": ">=10"
}
},
- "node_modules/cliui/node_modules/string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "node_modules/cmake-js/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/cmake-js/node_modules/yargs": {
+ "version": "17.7.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+ "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
"dev": true,
"dependencies": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
},
"engines": {
- "node": ">=6"
+ "node": ">=12"
}
},
- "node_modules/cliui/node_modules/strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "node_modules/cmake-js/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
- "dependencies": {
- "ansi-regex": "^4.1.0"
- },
"engines": {
- "node": ">=6"
+ "node": ">=12"
}
},
"node_modules/color-convert": {
@@ -502,6 +1410,27 @@
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true,
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -517,6 +1446,18 @@
"date-now": "^0.1.4"
}
},
+ "node_modules/console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+ "dev": true
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true
+ },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -555,6 +1496,69 @@
"node": ">=0.10.0"
}
},
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/default-require-extensions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz",
+ "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==",
+ "dev": true,
+ "dependencies": {
+ "strip-bom": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+ "dev": true
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
+ "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -620,91 +1624,301 @@
"domelementtype": "1"
}
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.374",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.374.tgz",
+ "integrity": "sha512-dNP9tQNTrjgVlSXMqGaj0BdrCS+9pcUvy5/emB6x8kh0YwCoDZ0Z4ce1+7aod+KhybHUd5o5LgKrc5al4kVmzQ==",
+ "dev": true
+ },
"node_modules/emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
- "node_modules/entities": {
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==",
+ "dev": true
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true
+ },
+ "node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs-minipass/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fs-minipass/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
- "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
- "node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "node_modules/gauge": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
+ "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
"dev": true,
+ "dependencies": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.3",
+ "console-control-strings": "^1.1.0",
+ "has-unicode": "^2.0.1",
+ "signal-exit": "^3.0.7",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.5"
+ },
"engines": {
- "node": ">=0.8.0"
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "node_modules/gauge/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
- "bin": {
- "esparse": "bin/esparse.js",
- "esvalidate": "bin/esvalidate.js"
- },
"engines": {
- "node": ">=4"
+ "node": ">=8"
}
},
- "node_modules/exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "node_modules/gauge/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/gauge/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
- "node": ">= 0.8.0"
+ "node": ">=8"
}
},
- "node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "node_modules/gauge/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
- "to-regex-range": "^5.0.1"
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "node_modules/gauge/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
+ "ansi-regex": "^5.0.1"
},
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">=8"
}
},
- "node_modules/flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "node_modules/gauge/node_modules/wide-align": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dev": true,
- "bin": {
- "flat": "cli.js"
+ "dependencies": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
}
},
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
"node_modules/get-caller-file": {
"version": "2.0.5",
@@ -715,6 +1929,12 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "dev": true
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -768,6 +1988,12 @@
"node": ">=4"
}
},
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
"node_modules/growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
@@ -786,6 +2012,12 @@
"node": ">=4"
}
},
+ "node_modules/has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+ "dev": true
+ },
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -795,6 +2027,12 @@
"he": "bin/he"
}
},
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
"node_modules/htmlparser2": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
@@ -808,6 +2046,26 @@
"readable-stream": "1.1"
}
},
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -824,6 +2082,12 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -896,6 +2160,91 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-hook": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz",
+ "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
+ "dev": true,
+ "dependencies": {
+ "append-transform": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+ "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+ "dev": true,
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -945,6 +2294,30 @@
"jshint": "bin/jshint"
}
},
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -966,6 +2339,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "dev": true
+ },
"node_modules/log-symbols": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
@@ -1033,33 +2412,189 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
- "node": ">=8"
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/memory-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz",
+ "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/memory-stream/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/memory-stream/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+ "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz",
+ "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
}
},
- "node_modules/log-symbols/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "node_modules/minizlib/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"dependencies": {
- "has-flag": "^4.0.0"
+ "yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
- "node_modules/minimatch": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
- "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "node_modules/minizlib/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
+ "bin": {
+ "mkdirp": "bin/cmd.js"
},
"engines": {
- "node": "*"
+ "node": ">=10"
}
},
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "dev": true
+ },
"node_modules/mocha": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz",
@@ -1217,6 +2752,75 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
+ "node_modules/napi-build-utils": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
+ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
+ "dev": true
+ },
+ "node_modules/node-abi": {
+ "version": "3.40.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz",
+ "integrity": "sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-abi/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-abi/node_modules/semver": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
+ "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-abi/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/node-addon-api": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
+ "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
+ "dev": true
+ },
+ "node_modules/node-api-headers": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-0.0.2.tgz",
+ "integrity": "sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
+ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+ "dev": true
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -1226,6 +2830,21 @@
"node": ">=0.10.0"
}
},
+ "node_modules/npmlog": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
+ "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+ "dev": true,
+ "dependencies": {
+ "are-we-there-yet": "^3.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^4.0.3",
+ "set-blocking": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1292,6 +2911,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -1316,6 +2941,59 @@
"node": ">=14"
}
},
+ "node_modules/prebuild-install": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
+ "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
+ "dev": true,
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp-classic": "^0.5.3",
+ "napi-build-utils": "^1.0.1",
+ "node-abi": "^3.3.0",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^4.0.0",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/proper-lockfile": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "retry": "^0.12.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "dev": true
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -1325,6 +3003,30 @@
"safe-buffer": "^5.1.0"
}
},
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
@@ -1386,6 +3088,30 @@
"node": ">=0.4.0"
}
},
+ "node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -1415,6 +3141,15 @@
"regexp-tree": "~0.1.1"
}
},
+ "node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/serialize-javascript": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
@@ -1430,6 +3165,76 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true
},
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/simple-get": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -1467,6 +3272,15 @@
"node": ">=4"
}
},
+ "node_modules/strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
@@ -1491,6 +3305,98 @@
"node": ">=4"
}
},
+ "node_modules/tar": {
+ "version": "6.1.13",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
+ "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==",
+ "dev": true,
+ "dependencies": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^4.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
+ "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+ "dev": true,
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-fs/node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar-stream/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/tar-stream/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/tar/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/tmp": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+ "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+ "dev": true,
+ "dependencies": {
+ "rimraf": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8.17.0"
+ }
+ },
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -1512,6 +3418,18 @@
"node": ">=8.0"
}
},
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/uglify-js": {
"version": "3.12.8",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz",
@@ -1524,6 +3442,57 @@
"node": ">=0.8.0"
}
},
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
+ "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/url-join": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
+ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
+ "dev": true
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -1621,6 +3590,12 @@
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true
},
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
"node_modules/yargs": {
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
diff --git a/package.json b/package.json
index dbe33f8d9..1d0c8e0ca 100644
--- a/package.json
+++ b/package.json
@@ -165,11 +165,13 @@
],
"scripts": {
"build": "./script/build-dist.sh && ./script/build-module.js",
+ "fuzz": "npx jazzer ./test/jazzer-fuzz-test.js --sync",
"test": "npm run build && ./script/test-all.sh"
},
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
+ "@jazzer.js/core": "^1.4.0",
"@playwright/test": "~1.32.2",
"jshint": "~2.13.6",
"mocha": "~8.2.0",
diff --git a/test/jazzer-test-fuzzing.js b/test/jazzer-test-fuzzing.js
new file mode 100644
index 000000000..c09e36f79
--- /dev/null
+++ b/test/jazzer-test-fuzzing.js
@@ -0,0 +1,15 @@
+const UAParser = require('ua-parser-js');
+
+module.exports.fuzz = function (buffer) {
+ const userAgent = buffer.toString();
+ const start = process.hrtime();
+ UAParser(userAgent);
+ const elapsed = process.hrtime(start);
+ const milisec = (elapsed[0]*1e3+elapsed[1]*1e-6).toFixed(3);
+ if (milisec > 1000) {
+ throw new Error(
+ `Potential ReDoS\n` +
+ `Time taken: ${milisec} ms.\n` +
+ `User agent: ${userAgent}`);
+ }
+};
\ No newline at end of file
From 102dc51683f9901028e3c450e84acd01c6db0032 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 27 Apr 2023 12:36:52 +0700
Subject: [PATCH 099/388] Update fuzzing test
---
test/{jazzer-test-fuzzing.js => jazzer-fuzz-test.js} | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
rename test/{jazzer-test-fuzzing.js => jazzer-fuzz-test.js} (67%)
diff --git a/test/jazzer-test-fuzzing.js b/test/jazzer-fuzz-test.js
similarity index 67%
rename from test/jazzer-test-fuzzing.js
rename to test/jazzer-fuzz-test.js
index c09e36f79..ee30cf26e 100644
--- a/test/jazzer-test-fuzzing.js
+++ b/test/jazzer-fuzz-test.js
@@ -1,9 +1,14 @@
+const { FuzzedDataProvider } = require('@jazzer.js/core');
const UAParser = require('ua-parser-js');
+const UA_MAX_LENGTH = 350;
module.exports.fuzz = function (buffer) {
- const userAgent = buffer.toString();
+ const data = new FuzzedDataProvider(buffer);
+ const userAgent = data.consumeString(UA_MAX_LENGTH, 'utf-8', true);
const start = process.hrtime();
+
UAParser(userAgent);
+
const elapsed = process.hrtime(start);
const milisec = (elapsed[0]*1e3+elapsed[1]*1e-6).toFixed(3);
if (milisec > 1000) {
From a519d2b879c08d2677a1f1da488e1b71bd2f5916 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Fri, 28 Apr 2023 07:06:45 +0700
Subject: [PATCH 100/388] Add new Action: publish to NPM
---
.github/workflows/publish-github-packages.yml | 20 +++-------------
.github/workflows/publish-npm-packages.yml | 23 +++++++++++++++++++
2 files changed, 26 insertions(+), 17 deletions(-)
create mode 100644 .github/workflows/publish-npm-packages.yml
diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml
index eaea41c37..25f1ca8f2 100644
--- a/.github/workflows/publish-github-packages.yml
+++ b/.github/workflows/publish-github-packages.yml
@@ -1,7 +1,4 @@
-# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
-# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
-
-name: Publish GitHub Package
+name: Publish to GitHub Package
on:
release:
@@ -9,17 +6,6 @@ on:
jobs:
build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
- with:
- node-version: 16
- - run: npm ci
- - run: npm test
-
- publish-gpr:
- needs: build
runs-on: ubuntu-latest
permissions:
contents: read
@@ -28,9 +14,9 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
- node-version: 16
+ node-version: '18.x'
registry-url: https://npm.pkg.github.com/
- run: npm ci
- run: npm publish
env:
- NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml
new file mode 100644
index 000000000..b4f35d974
--- /dev/null
+++ b/.github/workflows/publish-npm-packages.yml
@@ -0,0 +1,23 @@
+name: Publish to NPM
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: '18.x'
+ registry-url: 'https://registry.npmjs.org'
+ - run: npm install -g npm
+ - run: npm ci
+ - run: npm publish --provenance --access public
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
\ No newline at end of file
From df9144b49322c705e925b81e0947887f9bb69ec5 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 6 May 2023 19:53:51 +0700
Subject: [PATCH 101/388] Modify test scripts
---
package.json | 8 ++++++--
script/test-all.sh | 13 +++++++++----
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/package.json b/package.json
index 1d0c8e0ca..9913e2e9d 100644
--- a/package.json
+++ b/package.json
@@ -165,8 +165,12 @@
],
"scripts": {
"build": "./script/build-dist.sh && ./script/build-module.js",
- "fuzz": "npx jazzer ./test/jazzer-fuzz-test.js --sync",
- "test": "npm run build && ./script/test-all.sh"
+ "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync",
+ "test": "./script/test-all.sh",
+ "test:jshint": "jshint src && jshint script",
+ "test:lockfile-lint": "npx lockfile-lint -p package-lock.json",
+ "test:mocha": "mocha -R list test/mocha*js",
+ "test:playwright": "playwright test"
},
"devDependencies": {
"@babel/parser": "7.15.8",
diff --git a/script/test-all.sh b/script/test-all.sh
index b77df1a85..b47652bda 100755
--- a/script/test-all.sh
+++ b/script/test-all.sh
@@ -1,21 +1,26 @@
#!/usr/bin/env bash
+echo '
+- run build
+'
+npm run build || exit 1
+
echo '
- lint js code
'
-jshint src && jshint script || exit 1
+npm run test:jshint || exit 1
echo '
- test using mocha
'
-mocha -R list test/mocha*js || exit 1
+npm run test:mocha || exit 1
echo '
- test using playwright
'
-npx playwright test || exit 1
+npm run test:playwright || exit 1
echo '
- lint lockfile
'
-npx lockfile-lint -p package-lock.json || exit 1
\ No newline at end of file
+npm run test:lockfile-lint || exit 1
\ No newline at end of file
From 15d17e97a15d2aafdded34a5b094014e9b1c01f5 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 6 May 2023 21:21:13 +0700
Subject: [PATCH 102/388] Add some tests; Add new devices: Infinix, Tecno;
Improve detection: Xiaomi POCO
Source: https://www.useragents.me
---
src/main/ua-parser.js | 4 +-
test/specs/browser-all.json | 100 ++++++++
test/specs/cpu-all.json | 40 +++
test/specs/device-all.json | 492 ++++++++++++++++++++++++++++++------
4 files changed, 552 insertions(+), 84 deletions(-)
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index 65bab0292..06a03db3c 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -474,7 +474,7 @@
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
// Xiaomi
- /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO
+ /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
/\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
@@ -582,7 +582,7 @@
], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
// MIXED
- /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
+ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
/(hp) ([\w ]+\w)/i, // HP iPAQ
/(asus)-?(\w+)/i, // Asus
diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json
index 9ca190c61..507a9df37 100644
--- a/test/specs/browser-all.json
+++ b/test/specs/browser-all.json
@@ -199,6 +199,46 @@
"major" : "100"
}
},
+ {
+ "desc" : "Chrome 112.0 on Win10",
+ "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
+ "expect" :
+ {
+ "name" : "Chrome",
+ "version" : "112.0.0.0",
+ "major" : "112"
+ }
+ },
+ {
+ "desc" : "Chrome 112.0 on macOS",
+ "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
+ "expect" :
+ {
+ "name" : "Chrome",
+ "version" : "112.0.0.0",
+ "major" : "112"
+ }
+ },
+ {
+ "desc" : "Chrome 111.0 on Linux",
+ "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
+ "expect" :
+ {
+ "name" : "Chrome",
+ "version" : "111.0.0.0",
+ "major" : "111"
+ }
+ },
+ {
+ "desc" : "Chrome 111.0 on ChromeOS",
+ "ua" : "Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
+ "expect" :
+ {
+ "name" : "Chrome",
+ "version" : "111.0.0.0",
+ "major" : "111"
+ }
+ },
{
"desc" : "Chrome Headless",
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome Safari/537.36",
@@ -1328,6 +1368,56 @@
"major" : "22"
}
},
+ {
+ "desc" : "Yandex",
+ "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 YaBrowser/23.3.0.2246 Yowser/2.5 Safari/537.36",
+ "expect" :
+ {
+ "name" : "Yandex",
+ "version" : "23.3.0.2246",
+ "major" : "23"
+ }
+ },
+ {
+ "desc" : "Yandex on Android",
+ "ua" : "Mozilla/5.0 (Linux; arm_64; Android 13; SM-G965F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 YaBrowser/21.3.4.59 Mobile Safari/537.36",
+ "expect" :
+ {
+ "name" : "Yandex",
+ "version" : "21.3.4.59",
+ "major" : "21"
+ }
+ },
+ {
+ "desc" : "Yandex on iPhone",
+ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/604.1",
+ "expect" :
+ {
+ "name" : "Yandex",
+ "version" : "23.3.3.330",
+ "major" : "23"
+ }
+ },
+ {
+ "desc" : "Yandex on iPad",
+ "ua" : "Mozilla/5.0 (iPad; CPU OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/605.1",
+ "expect" :
+ {
+ "name" : "Yandex",
+ "version" : "23.3.3.330",
+ "major" : "23"
+ }
+ },
+ {
+ "desc" : "Yandex on iPod",
+ "ua" : "Mozilla/5.0 (iPod touch; CPU iPhone 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/605.1",
+ "expect" :
+ {
+ "name" : "Yandex",
+ "version" : "23.3.3.330",
+ "major" : "23"
+ }
+ },
{
"desc" : "Puffin",
"ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo P2a42 Build/MMB29M; en-us) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Puffin/6.0.8.15804AP",
@@ -1428,6 +1518,16 @@
"major" : "1"
}
},
+ {
+ "desc" : "Firefox on iOS",
+ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/112.0 Mobile/15E148 Safari/605.1.15",
+ "expect" :
+ {
+ "name" : "Mobile Firefox",
+ "version" : "112.0",
+ "major" : "112"
+ }
+ },
{
"desc" : "Firefox iOS using iPad",
"ua" : "Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4",
diff --git a/test/specs/cpu-all.json b/test/specs/cpu-all.json
index 2a7d18920..da86557f0 100644
--- a/test/specs/cpu-all.json
+++ b/test/specs/cpu-all.json
@@ -23,6 +23,46 @@
"architecture" : "amd64"
}
},
+ {
+ "desc" : "Vivaldi on Windows",
+ "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18",
+ "expect" :
+ {
+ "architecture" : "amd64"
+ }
+ },
+ {
+ "desc" : "Vivaldi on Windows",
+ "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18",
+ "expect" :
+ {
+ "architecture" : "amd64"
+ }
+ },
+ {
+ "desc" : "Vivaldi on Linux",
+ "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18",
+ "expect" :
+ {
+ "architecture" : "amd64"
+ }
+ },
+ {
+ "desc" : "Vivaldi on Linux",
+ "ua" : "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18",
+ "expect" :
+ {
+ "architecture" : "ia32"
+ }
+ },
+ {
+ "desc": "Xiaomi POCO M2 Pro",
+ "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36",
+ "expect" :
+ {
+ "architecture" : "arm64"
+ }
+ },
{
"desc" : "win64",
"ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)",
diff --git a/test/specs/device-all.json b/test/specs/device-all.json
index 1c54884c0..6a96d800e 100644
--- a/test/specs/device-all.json
+++ b/test/specs/device-all.json
@@ -1,4 +1,14 @@
-[{
+[
+ {
+ "desc": "K",
+ "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "K",
+ "type": "mobile"
+ }
+ },
+ {
"desc": "ASUS Nexus 7",
"ua": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36",
"expect": {
@@ -187,6 +197,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Blackview 4900Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 12; BV4900Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "BV4900Pro",
+ "type": "mobile"
+ }
+ },
{
"desc": "Desktop (IE11 with Tablet string)",
"ua": "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; GWX:MANAGED; rv:11.0) like Gecko",
@@ -413,31 +432,31 @@
}
},
{
- "desc": "Huawei Mate 20 X",
- "ua": "Mozilla/5.0 (Linux; Android 9; EVR-L29 Build/HUAWEIEVR-L29; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36",
- "expect": {
- "vendor": "Huawei",
- "model": "EVR-L29",
- "type": "mobile"
- }
+ "desc": "Huawei Mate 20 X",
+ "ua": "Mozilla/5.0 (Linux; Android 9; EVR-L29 Build/HUAWEIEVR-L29; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Huawei",
+ "model": "EVR-L29",
+ "type": "mobile"
+ }
},
{
- "desc": "Huawei Mate 20 Pro",
- "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36",
- "expect": {
- "vendor": "Huawei",
- "model": "LYA-L09",
- "type": "mobile"
- }
+ "desc": "Huawei Mate 20 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Huawei",
+ "model": "LYA-L09",
+ "type": "mobile"
+ }
},
{
- "desc": "Huawei Mate 20 Pro",
- "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36",
- "expect": {
- "vendor": "Huawei",
- "model": "LYA-AL00",
- "type": "mobile"
- }
+ "desc": "Huawei Mate 20 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Huawei",
+ "model": "LYA-AL00",
+ "type": "mobile"
+ }
},
{
"desc": "Huawei Mate 20 Pro",
@@ -447,8 +466,8 @@
"model": "LYA-AL10",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Mate 20 Pro",
"ua": "Mozilla/5.0 (Linux; Android 9; LYA-L0C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36",
"expect": {
@@ -456,8 +475,8 @@
"model": "LYA-L0C",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Mate 20 Pro",
"ua": "Mozilla/5.0 (Linux; Android 9; LYA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36",
"expect": {
@@ -465,8 +484,8 @@
"model": "LYA-L29",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Mate 20 Pro",
"ua": "Mozilla/5.0 (Linux; Android 9; LYA-TL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36",
"expect": {
@@ -475,6 +494,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Huawei Mate 50 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 12; DCO-LX9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Huawei",
+ "model": "DCO-LX9",
+ "type": "mobile"
+ }
+ },
{
"desc": "Huawei P20 Lite",
"ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36",
@@ -485,13 +513,13 @@
}
},
{
- "desc": "Huawei P20",
- "ua": "Mozilla/5.0 (Linux; Android 8.1.0; EML-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36",
- "expect": {
- "vendor": "Huawei",
- "model": "EML-L29",
- "type": "mobile"
- }
+ "desc": "Huawei P20",
+ "ua": "Mozilla/5.0 (Linux; Android 8.1.0; EML-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Huawei",
+ "model": "EML-L29",
+ "type": "mobile"
+ }
},
{
"desc": "Huawei P20 Pro",
@@ -564,8 +592,8 @@
"model": "YAL-L21",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Nova 5T",
"ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
"expect": {
@@ -573,8 +601,8 @@
"model": "YAL-L61",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Nova 5T",
"ua": "Mozilla/5.0 (Linux; Android 10; YAL-L71) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
"expect": {
@@ -582,8 +610,8 @@
"model": "YAL-L71",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Nova 5T",
"ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
"expect": {
@@ -591,8 +619,8 @@
"model": "YAL-L61D",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Nova 5T",
"ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
"expect": {
@@ -600,8 +628,8 @@
"model": "YALE-L61A",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Nova 5T",
"ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
"expect": {
@@ -609,8 +637,8 @@
"model": "YALE-L61D",
"type": "mobile"
}
- },
- {
+ },
+ {
"desc": "Huawei Nova 5T",
"ua": "Mozilla/5.0 (Linux; Android 10; YALE-L71A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
"expect": {
@@ -745,6 +773,51 @@
"type": "mobile"
}
},
+ {
+ "desc": "Infinix Hot 7 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 9; Infinix X625C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Infinix",
+ "model": "X625C",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Infinix Hot 10T",
+ "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X689C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Infinix",
+ "model": "X689C",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Infinix Hot 11s",
+ "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X6812 Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Infinix",
+ "model": "X6812",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Infinix Smart 5",
+ "ua": "Mozilla/5.0 (Linux; Android 10; Infinix X657C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Infinix",
+ "model": "X657C",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Infinix Zero 5G",
+ "ua": "Mozilla/5.0 (Linux; Android 12; Infinix X6815B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Infinix",
+ "model": "X6815B",
+ "type": "mobile"
+ }
+ },
{
"desc": "Apple Desktop",
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15",
@@ -925,6 +998,24 @@
"type": "mobile"
}
},
+ {
+ "desc": "LG K40",
+ "ua": "Mozilla/5.0 (Linux; Android 10; LM-X420) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "LG",
+ "model": "LM-X420",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "LG Stylo 4",
+ "ua": "Mozilla/5.0 (Linux; Android 10; LM-Q710(FGN)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "LM-Q710(FGN)",
+ "type": "mobile"
+ }
+ },
{
"desc": "LG Stylo 5",
"ua": "Mozilla/5.0 (Linux; Android 9; LM-Q720) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36",
@@ -943,6 +1034,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "LG K20",
+ "ua": "Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:111.0) Gecko/111.0 Firefox/111.0",
+ "expect": {
+ "vendor": "LG",
+ "model": "M255",
+ "type": "mobile"
+ }
+ },
{
"desc": "LG K500",
"ua": "Mozilla/5.0 (Linux; Android 6.0.1; LG-K500 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
@@ -1061,23 +1161,30 @@
}
},
{
- "desc" : "Meizu M3S",
- "ua" : "Mozilla/5.0 (X11; Linux; Android 5.1; MZ-M3s Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrom/45.0.2454.94 Mobile Safari/537.36",
- "expect" :
- {
- "vendor" : "Meizu",
- "model" : "M3s",
- "type" : "mobile"
+ "desc": "Motorola Moto Z3 Play",
+ "ua": "Mozilla/5.0 (Linux; Android 9; Moto Z3 Play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Motorola",
+ "model": "Moto Z3 Play",
+ "type": "mobile"
}
},
{
- "desc" : "Microsoft Lumia 950",
- "ua" : "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586",
- "expect" :
- {
- "vendor" : "Microsoft",
- "model" : "Lumia 950",
- "type" : "mobile"
+ "desc": "Meizu M3S",
+ "ua": "Mozilla/5.0 (X11; Linux; Android 5.1; MZ-M3s Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrom/45.0.2454.94 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Meizu",
+ "model": "M3s",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Microsoft Lumia 950",
+ "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586",
+ "expect": {
+ "vendor": "Microsoft",
+ "model": "Lumia 950",
+ "type": "mobile"
}
},
{
@@ -1215,6 +1322,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Nokia 7",
+ "ua": "Mozilla/5.0 (Linux; Android 11; Nokia 7.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Nokia",
+ "model": "7.2",
+ "type": "mobile"
+ }
+ },
{
"desc": "Nokia N9",
"ua": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
@@ -1314,6 +1430,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "OnePlus 7T Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36 EdgA/110.0.1587.66",
+ "expect": {
+ "vendor": "undefined",
+ "model": "HD1913",
+ "type": "mobile"
+ }
+ },
{
"desc": "OnePlus 8T",
"ua": "Mozilla/5.0 (Linux; Android 11; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36",
@@ -1332,6 +1457,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "OnePlus 10RT",
+ "ua": "Mozilla/5.0 (Linux; Android 13; CPH2413) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "OPPO",
+ "model": "CPH2413",
+ "type": "mobile"
+ }
+ },
{
"desc": "OnePlus Nord N100",
"ua": "Mozilla/5.0 (Linux; Android 10; BE2015 Build/QKQ1.200719.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36",
@@ -1494,6 +1628,33 @@
"type": "mobile"
}
},
+ {
+ "desc": "Realme 3 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 11; RMX1851) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Realme",
+ "model": "RMX1851",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Realme 9 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 13; RMX3471) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "RMX3471",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Realme GT Master",
+ "ua": "Mozilla/5.0 (Linux; Android 13; RMX3363) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "RMX3363",
+ "type": "mobile"
+ }
+ },
{
"desc": "Panasonic T31",
"ua": "Mozilla/5.0 (Linux; Android 4.2.2; Panasonic T31 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Mobile Safari/537.36 ",
@@ -1683,6 +1844,24 @@
"type": "mobile"
}
},
+ {
+ "desc": "Samsung Galaxy A50s",
+ "ua": "Mozilla/5.0 (Linux; Android 11; SM-A507FN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Samsung",
+ "model": "SM-A507FN",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Samsung Galaxy A52s",
+ "ua": "Mozilla/5.0 (Linux; Android 13; SM-A528B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Samsung",
+ "model": "SM-A528B",
+ "type": "mobile"
+ }
+ },
{
"desc": "Samsung Galaxy A80",
"ua": "Mozilla/5.0 (Linux; Android 9; SM-A805F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.112 Mobile Safari/537.36",
@@ -1736,7 +1915,7 @@
"model": "SCG01",
"type": "mobile"
}
- },
+ },
{
"desc": "Samsung Galaxy Note 10+",
"ua": "Mozilla/5.0 (Linux; Android 9; SM-N976V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36",
@@ -1755,6 +1934,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Samsung C8",
+ "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-C7108) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Samsung",
+ "model": "SM-C7108",
+ "type": "mobile"
+ }
+ },
{
"desc": "Samsung Galaxy Note 8",
"ua": "Mozilla/5.0 (Linux; Android 4.2.2; GT-N5100 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Safari/537.36",
@@ -1840,9 +2028,9 @@
"desc": "Samsung Note 10.1",
"ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-P605) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36",
"expect": {
- "vendor": "Samsung",
- "model": "SM-P605",
- "type": "tablet"
+ "vendor": "Samsung",
+ "model": "SM-P605",
+ "type": "tablet"
}
},
{
@@ -1953,6 +2141,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Sony G8141 (Xperia XZ1)",
+ "ua": "Mozilla/5.0 (Linux; Android 9; SO-01K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Sony",
+ "model": "SO-01K",
+ "type": "mobile"
+ }
+ },
{
"desc": "Sony G8141 (Xperia XZ Premium)",
"ua": "Mozilla/5.0 (Linux; Android 8.0.0; G8141) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36",
@@ -2116,40 +2313,45 @@
}
},
{
- "desc" : "Tesla",
- "ua" : "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1",
- "expect" :
- {
+ "desc": "Tecno KC8",
+ "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "TECNO",
+ "model": "KC8",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Tesla",
+ "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1",
+ "expect": {
"vendor": "Tesla",
"model": "undefined",
"type": "embedded"
}
},
{
- "desc" : "Tesla",
- "ua" : "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.16.2.1-e99c70fff409",
- "expect" :
- {
+ "desc": "Tesla",
+ "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.16.2.1-e99c70fff409",
+ "expect": {
"vendor": "Tesla",
"model": "undefined",
"type": "embedded"
}
},
{
- "desc" : "TechniSAT Digit ISIO S SAT receiver",
- "ua" : "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50",
- "expect" :
- {
+ "desc": "TechniSAT Digit ISIO S SAT receiver",
+ "ua": "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50",
+ "expect": {
"vendor": "TechniSat",
"model": "Digit ISIO S",
"type": "smarttv"
}
},
{
- "desc" : "TechniSAT MultyVision SmartTV",
- "ua" : "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50",
- "expect" :
- {
+ "desc": "TechniSAT MultyVision SmartTV",
+ "ua": "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50",
+ "expect": {
"vendor": "TechniSat",
"model": "MultyVision ISIO",
"type": "smarttv"
@@ -2317,6 +2519,51 @@
"type": "mobile"
}
},
+ {
+ "desc": "Xiaomi POCO X3 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 11; M2102J20SI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Xiaomi",
+ "model": "M2102J20SI",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Xiaomi POCO X3 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 12; M2102J20SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Xiaomi",
+ "model": "M2102J20SG",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Xiaomi POCO X3 NFC",
+ "ua": "Mozilla/5.0 (Linux; Android 12; M2007J20CG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Xiaomi",
+ "model": "M2007J20CG",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Xiaomi POCO M2 Pro",
+ "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Xiaomi",
+ "model": "POCO M2 Pro",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "Xiaomi POCO M3",
+ "ua": "Mozilla/5.0 (Linux; Android 10; M2010J19CI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Xiaomi",
+ "model": "M2010J19CI",
+ "type": "mobile"
+ }
+ },
{
"desc": "Xiaomi Redmi 4A",
"ua": "Mozilla/5.0 (Linux; Android 6.0; Redmi 4A Build/MMB29M; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36",
@@ -2326,6 +2573,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Xiaomi Redmi 10C",
+ "ua": "Mozilla/5.0 (Linux; Android 12; 220333QAG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "220333QAG",
+ "type": "mobile"
+ }
+ },
{
"desc": "Xiaomi Redmi K30 5G",
"ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36",
@@ -2362,6 +2618,51 @@
"type": "mobile"
}
},
+ {
+ "desc": "XiaoMi Redmi Note 9S",
+ "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Xiaomi",
+ "model": "Redmi Note 9S",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "XiaoMi Redmi Note 10 5G",
+ "ua": "Mozilla/5.0 (Linux; Android 12; M2103K19C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "M2103K19C",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "XiaoMi Redmi Note 10 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "M2101K6P",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "XiaoMi Redmi Note 10 Pro",
+ "ua": "Mozilla/5.0 (Linux; Android 12; M2101K6G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "undefined",
+ "model": "M2101K6G",
+ "type": "mobile"
+ }
+ },
+ {
+ "desc": "XiaoMi Redmi Note 8",
+ "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Xiaomi",
+ "model": "Redmi Note 8",
+ "type": "mobile"
+ }
+ },
{
"desc": "PlayStation 4",
"ua": "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)",
@@ -2461,6 +2762,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Samsung Galaxy J7 Prime",
+ "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Samsung",
+ "model": "SM-G610F",
+ "type": "mobile"
+ }
+ },
{
"desc": "Samsung Galaxy S6",
"ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G920I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36",
@@ -2907,6 +3217,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Vivo Y93",
+ "ua": "Mozilla/5.0 (Linux; Android 8.1.0; vivo 1814) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Vivo",
+ "model": "1814",
+ "type": "mobile"
+ }
+ },
{
"desc": "Vivo Y97",
"ua": "Mozilla/5.0 (Linux; Android 8.1.0; V1813T Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0",
@@ -2925,6 +3244,15 @@
"type": "mobile"
}
},
+ {
+ "desc": "Vivo 1906 (Y11)",
+ "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1906) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Vivo",
+ "model": "1906",
+ "type": "mobile"
+ }
+ },
{
"desc": "Unknown Mobile using Firefox",
"ua": "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0",
@@ -3006,4 +3334,4 @@
"type": "mobile"
}
}
-]
+]
\ No newline at end of file
From b20d09655cf01657d00e38e334a757c25b176175 Mon Sep 17 00:00:00 2001
From: Runar Heggset
Date: Wed, 3 May 2023 11:28:54 +0200
Subject: [PATCH 103/388] Fix Amazon Fire TV device detection
---
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 65bab0292..45dddb000 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -627,7 +627,7 @@
], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
/crkey/i // Google Chromecast
], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
- /droid.+aft(\w)( bui|\))/i // Fire TV
+ /droid.+aft(\w+)( bui|\))/i // Fire TV
], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
/\(dtv[\);].+(aquos)/i,
/(aquos-tv[\w ]+)\)/i // Sharp
diff --git a/test/specs/device-all.json b/test/specs/device-all.json
index 1c54884c0..b43969c41 100644
--- a/test/specs/device-all.json
+++ b/test/specs/device-all.json
@@ -2772,6 +2772,15 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Amazon Fire TV",
+ "ua": "Mozilla/5.0 (Linux; Android 9; AFTKA Build/PS7633.3445N; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.160 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Amazon",
+ "model": "KA",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Android TV",
"ua": "Mozilla/5.0 (Linux; Android 10; 2020/2021 UHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36",
From 072a82b87ba35cc15ae10fd02d905f4b78a8ce05 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 27 May 2023 20:45:43 +0700
Subject: [PATCH 104/388] Add a new submodule: `ua-parser-helpers` with a
method: `isFrozenUA()` to match a string with a frozen user-agent pattern
---
package.json | 4 +++
script/build-module.js | 10 +++++++-
src/helpers/ua-parser-helpers.js | 42 ++++++++++++++++++++++++++++++++
test/mocha-test-helpers.js | 35 ++++++++++++++++++++++++++
4 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 src/helpers/ua-parser-helpers.js
create mode 100644 test/mocha-test-helpers.js
diff --git a/package.json b/package.json
index 9913e2e9d..ea8379f8c 100644
--- a/package.json
+++ b/package.json
@@ -157,6 +157,10 @@
"./extensions": {
"require": "./src/extensions/ua-parser-extensions.js",
"import": "./src/extensions/ua-parser-extensions.mjs"
+ },
+ "./helpers": {
+ "require": "./src/helpers/ua-parser-helpers.js",
+ "import": "./src/helpers/ua-parser-helpers.mjs"
}
},
"files": [
diff --git a/script/build-module.js b/script/build-module.js
index bbeb72cc3..d7f5a59ae 100755
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -16,6 +16,11 @@ const PATH = {
src : 'src/extensions/ua-parser-extensions.js',
dest : 'src/extensions/ua-parser-extensions.mjs',
title : 'extensions'
+ },
+ helpers : {
+ src : 'src/helpers/ua-parser-helpers.js',
+ dest : 'src/helpers/ua-parser-helpers.mjs',
+ title : 'helpers'
}
};
const generateMJS = (module, replacers) => {
@@ -46,4 +51,7 @@ generateMJS('main', [
generateMJS('enums', [[/module\.exports =/ig, 'export']]);
// ua-parser-extension.mjs
-generateMJS('extensions', [[/module\.exports =/ig, 'export']]);
\ No newline at end of file
+generateMJS('extensions', [[/module\.exports =/ig, 'export']]);
+
+// ua-parser-helpers.mjs
+generateMJS('helpers', [[/module\.exports =/ig, 'export']]);
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js
new file mode 100644
index 000000000..9e6c3395a
--- /dev/null
+++ b/src/helpers/ua-parser-helpers.js
@@ -0,0 +1,42 @@
+///////////////////////////////////////////////
+/* Helpers for UAParser.js v2.0.0-alpha.2
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+//////////////////////////////////////////////
+
+/*jshint esversion: 6 */
+
+/*
+ # Reference:
+ https://www.chromium.org/updates/ua-reduction/
+
+ # Desktop
+ ---
+ Format:
+ Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
+
+ Possible values:
+ - Windows NT 10.0; Win64; x64
+ - Macintosh; Intel Mac OS X 10_15_7
+ - X11; Linux x86_64
+ - X11; CrOS x86_64 14541.0.0
+ - Fuchsia
+
+ # Mobile & Tablet: (except iOS/Android WebView)
+ ---
+ Format:
+ Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
+
+ Possible values:
+ - "Mobile"
+ - "" (empty string for Tablets & Desktop)
+*/
+
+const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/;
+
+const isFrozenUA = str => frozenUA.test(str);
+
+module.exports = {
+ isFrozenUA
+};
\ No newline at end of file
diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js
new file mode 100644
index 000000000..b954e73cc
--- /dev/null
+++ b/test/mocha-test-helpers.js
@@ -0,0 +1,35 @@
+const { isFrozenUA } = require('ua-parser-js/helpers');
+const assert = require('assert');
+
+describe('isFrozenUA', () => {
+ it('Returns whether a user agent is frozen', () => {
+
+ const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
+ const freezedWindowsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
+
+ const regularMacUA = "";
+ const freezedMacUA = "";
+
+ const regularLinuxUA = "";
+ const freezedLinuxUA = "";
+
+ const regularCrOSUA = "";
+ const freezedCrOSUA = "";
+
+ const regularFuchsiaUA = "";
+ const freezedFuchsiaUA = "";
+
+ const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
+ const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
+
+ const regularTabletUA = "Mozilla/5.0 (Linux; Android 9; SM-T810) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
+ const freezedTabletUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
+
+ assert.strictEqual(isFrozenUA(regularWindowsUA), false);
+ assert.strictEqual(isFrozenUA(freezedWindowsUA), true);
+ assert.strictEqual(isFrozenUA(regularMobileUA), false);
+ assert.strictEqual(isFrozenUA(freezedMobileUA), true);
+ assert.strictEqual(isFrozenUA(regularTabletUA), false);
+ assert.strictEqual(isFrozenUA(freezedTabletUA), true);
+ });
+});
\ No newline at end of file
From 153831d2edb73db1f066c5638da733d302027a1e Mon Sep 17 00:00:00 2001
From: JBYoshi <12983479+JBYoshi@users.noreply.github.com>
Date: Fri, 2 Jun 2023 22:05:01 -0500
Subject: [PATCH 105/388] Add Snapchat user agent.
---
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 06a03db3c..211b2ec89 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -347,7 +347,7 @@
/(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
/safari (line)\/([\w\.]+)/i, // Line App for iOS
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
- /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
+ /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
], [NAME, VERSION], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json
index 507a9df37..2ef402e6f 100644
--- a/test/specs/browser-all.json
+++ b/test/specs/browser-all.json
@@ -1869,5 +1869,15 @@
"version" : "41.0",
"major" : "41"
}
+ },
+ {
+ "desc" : "Snapchat",
+ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Snapchat/12.33.0.36 (like Safari/8614.1.25.0.31, panda)",
+ "expect" :
+ {
+ "name" : "Snapchat",
+ "version" : "12.33.0.36",
+ "major" : "12"
+ }
}
]
\ No newline at end of file
From f2e4b242ceaa9bcdafd731066fe665c2b28132f5 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 7 Aug 2023 21:35:46 +0700
Subject: [PATCH 106/388] Update sponsors section in readme
---
readme.md | 26 +++++++++++---------------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/readme.md b/readme.md
index 7b669d966..43d66e94a 100644
--- a/readme.md
+++ b/readme.md
@@ -20,7 +20,9 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
* Author : Faisal Salman <>
* Demo : https://faisalman.github.io/ua-parser-js
* Source : https://github.com/faisalman/ua-parser-js
-* Documentation : https://faisalman.github.io/ua-parser-js-docs/v2
+* Documentation :
+ * v1 : https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation
+ * v2 : https://faisalman.github.io/ua-parser-js-docs/v2
***
@@ -30,14 +32,6 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
-
-↗ @51degrees/ua-parser-js
-
-
-UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.
This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).
Visit ↗ 51Degrees UAParser to get started.
-
-
-
↗ Become a sponsor
@@ -129,8 +123,9 @@ NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb,
Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris,
Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, [Mobile] Safari,
Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim,
-SlimBrowser, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser, UP.Browser, Viera,
-Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ...
+SlimBrowser, Snapchat, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser,
+UP.Browser, Viera, Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m,
+Whale Browser, ...
# 'browser.version' determined dynamically
```
@@ -152,10 +147,11 @@ console, mobile, tablet, smarttv, wearable, embedded
# Possible 'device.vendor':
Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell,
-Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Kobo,
-Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus,
-OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp,
-Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ...
+Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Infinix, Jolla,
+Kobo, Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia,
+OnePlus, OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung,
+Sharp, Siemens, Sony[Ericsson], Sprint, Tecno, Tesla, Vivo, Vodafone, Xbox, Xiaomi,
+Zebra, ZTE, ...
# 'device.model' determined dynamically
```
From 22eae9f70cbbecf4d425c5195081657b01bf1274 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 7 Aug 2023 21:39:21 +0700
Subject: [PATCH 107/388] [extensions] Add GPTBot to Bots
---
src/extensions/ua-parser-extensions.js | 3 +++
test/mocha-test-extension.js | 2 ++
2 files changed, 5 insertions(+)
diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js
index 30de6d1b0..b7ce27ec8 100644
--- a/src/extensions/ua-parser-extensions.js
+++ b/src/extensions/ua-parser-extensions.js
@@ -26,6 +26,9 @@ const Bots = Object.freeze({
// 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']]
]
diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js
index 451a99c0a..e83ad14e1 100644
--- a/test/mocha-test-extension.js
+++ b/test/mocha-test-extension.js
@@ -9,6 +9,7 @@ const Ext = require('ua-parser-js/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)';
@@ -19,6 +20,7 @@ describe('Bots', () => {
const botParser = new UAParser(Ext.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(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"});
assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"});
From 420bf1066c02bcf9d4243a9e0c9da8acf00b9811 Mon Sep 17 00:00:00 2001
From: chenyuan-new <53860479+chenyuan-new@users.noreply.github.com>
Date: Tue, 8 Aug 2023 16:22:10 +0800
Subject: [PATCH 108/388] fix: remove duplicated BRANDS input when call
setProps in UACHData func (#663)
---
src/main/ua-parser.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index 45dddb000..a8ed359e7 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -925,7 +925,6 @@
setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
- [BRANDS, itemListToArray(uach[CH_HEADER])],
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
From f76d8983ca08a83e7c39a7d5ec8bf1ee8ffdff98 Mon Sep 17 00:00:00 2001
From: Runar Heggset
Date: Wed, 3 May 2023 11:28:54 +0200
Subject: [PATCH 109/388] Fix Amazon Fire TV device detection
---
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 211b2ec89..56aaf58b1 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -627,7 +627,7 @@
], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
/crkey/i // Google Chromecast
], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
- /droid.+aft(\w)( bui|\))/i // Fire TV
+ /droid.+aft(\w+)( bui|\))/i // Fire TV
], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
/\(dtv[\);].+(aquos)/i,
/(aquos-tv[\w ]+)\)/i // Sharp
diff --git a/test/specs/device-all.json b/test/specs/device-all.json
index 6a96d800e..093faa92a 100644
--- a/test/specs/device-all.json
+++ b/test/specs/device-all.json
@@ -3082,6 +3082,15 @@
"type": "smarttv"
}
},
+ {
+ "desc": "Amazon Fire TV",
+ "ua": "Mozilla/5.0 (Linux; Android 9; AFTKA Build/PS7633.3445N; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.160 Mobile Safari/537.36",
+ "expect": {
+ "vendor": "Amazon",
+ "model": "KA",
+ "type": "smarttv"
+ }
+ },
{
"desc": "Android TV",
"ua": "Mozilla/5.0 (Linux; Android 10; 2020/2021 UHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36",
From 3cb567c1541c958e23f94a0c73aeaf4eab0306c7 Mon Sep 17 00:00:00 2001
From: chenyuan-new <53860479+chenyuan-new@users.noreply.github.com>
Date: Tue, 8 Aug 2023 16:22:10 +0800
Subject: [PATCH 110/388] fix: remove duplicated BRANDS input when call
setProps in UACHData func (#663)
---
src/main/ua-parser.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index 56aaf58b1..861bb105b 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -925,7 +925,6 @@
setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
- [BRANDS, itemListToArray(uach[CH_HEADER])],
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
From 6e26e38247f35e31558daabaf608620eb2451b8a Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 14 Aug 2023 12:28:36 +0700
Subject: [PATCH 111/388] [extension] Add Axios, jsdom, Scrapy. Also fixes #627
#156 #217 #227
Axios: `axios/VERSION`
https://www.zenrows.com/blog/axios-user-agent#what-is-axios-user-agent
JSDOM: `Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}`
https://github.com/jsdom/jsdom
Scrapy: `Scrapy/VERSION (+https://scrapy.org)`
https://docs.scrapy.org/en/master/topics/settings.html
---
src/extensions/ua-parser-extensions.js | 10 +++++++++-
test/mocha-test-extension.js | 8 ++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js
index b7ce27ec8..3ace7c1bb 100644
--- a/src/extensions/ua-parser-extensions.js
+++ b/src/extensions/ua-parser-extensions.js
@@ -230,11 +230,19 @@ const MediaPlayers = Object.freeze({
]
});
+const Modules = Object.freeze({
+ browser : [
+ // Axios/jsdom/Scrapy
+ [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']]
+ ]
+});
+
module.exports = {
Apps,
Bots,
CLIs,
ExtraDevices,
Emails,
- MediaPlayers
+ MediaPlayers,
+ Modules
};
\ No newline at end of file
diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js
index e83ad14e1..605bf6389 100644
--- a/test/mocha-test-extension.js
+++ b/test/mocha-test-extension.js
@@ -17,6 +17,9 @@ describe('Bots', () => {
const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)';
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(Ext.Bots);
assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"});
@@ -34,6 +37,11 @@ describe('Bots', () => {
const emailParser = new UAParser(Ext.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(Ext.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"});
});
});
From d168b75a3a21d9d3cb1292fc7a2e36738468fb7d Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 17 Aug 2023 11:29:18 +0700
Subject: [PATCH 112/388] Bump version 2.0.0-alpha.3
---
bower.json | 2 +-
changelog.md | 7 +
dist/ua-parser.min.js | 4 +-
dist/ua-parser.pack.js | 4 +-
images/51degrees.svg | 19 --
package-lock.json | 4 +-
package.js | 2 +-
package.json | 2 +-
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 | 236 ++++++++++++++++++------
src/helpers/ua-parser-helpers.js | 2 +-
src/helpers/ua-parser-helpers.mjs | 46 +++++
src/main/ua-parser.js | 14 +-
src/main/ua-parser.mjs | 26 +--
16 files changed, 262 insertions(+), 112 deletions(-)
delete mode 100644 images/51degrees.svg
create mode 100644 src/helpers/ua-parser-helpers.mjs
diff --git a/bower.json b/bower.json
index 0f5d520fd..a8c0ae384 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "ua-parser-js",
- "version": "2.0.0-alpha.2",
+ "version": "2.0.0-alpha.3",
"authors": [
"Faisal Salman "
],
diff --git a/changelog.md b/changelog.md
index ac8e493be..5339d6cb7 100644
--- a/changelog.md
+++ b/changelog.md
@@ -13,6 +13,13 @@
- Add support for ES module `import { UAParser } from 'ua-parser-js'`
- Provide Enums `'ua-parser-js/enums'`
- Provide Extensions `'ua-parser-js/extensions'`
+ - Provide Helpers `'ua-parser-js/helpers'`
+
+## Version 2.0.0-alpha.3
+- Add `withFeatureCheck()` method
+- Add `isFrozenUA()` method in `helpers` submodule
+- Add `MediaPlayers` & `Modules` in `extensions` submodule
+- Fix issue with ESM import
## Version 2.0.0-alpha.2
- Fix browser result always returning Chromium when using `withClientHints()`
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index e18ba06a7..46cf21f2f 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
-/* UAParser.js v2.0.0-alpha.2
+/* UAParser.js v2.0.0-alpha.3
Copyright © 2012-2023 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.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=350,BRANDS="brands",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_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","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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 ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}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=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=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,HTTP_UACH).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],HTTP_UACH).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){userAgent=typeof ua===STR_TYPE&&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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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-alpha.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",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",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_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,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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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 ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}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(typeof ua===STR_TYPE)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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 59ce392a0..0ba28a22b 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
-/* UAParser.js v2.0.0-alpha.2
+/* UAParser.js v2.0.0-alpha.3
Copyright © 2012-2023 Faisal Salman
MIT License */
-!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.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;tS?ki(i,S):i),this}]]).setUA(o),this}zi.VERSION="2.0.0-alpha.3",zi.BROWSER=e([h,m,u]),zi.CPU=e([g]),zi.DEVICE=e([p,o,f,r,v,x,a,k,y]),zi.ENGINE=zi.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===l&&define.amd?define(function(){return zi}):typeof i!==b&&(i.UAParser=zi);var Ni,Oi=typeof i!==b&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ni=new zi,Oi.ua=Ni.getResult(),Oi.ua.get=function(){return Ni.getUA()},Oi.ua.set=function(i){Ni.setUA(i);var e,t=Ni.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
diff --git a/images/51degrees.svg b/images/51degrees.svg
deleted file mode 100644
index c3393f646..000000000
--- a/images/51degrees.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/package-lock.json b/package-lock.json
index 694dedaa2..f0d514f4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "ua-parser-js",
- "version": "2.0.0-alpha.2",
+ "version": "2.0.0-alpha.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ua-parser-js",
- "version": "2.0.0-alpha.2",
+ "version": "2.0.0-alpha.3",
"funding": [
{
"type": "opencollective",
diff --git a/package.js b/package.js
index cb7f57ac0..d33aba3f2 100644
--- a/package.js
+++ b/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'faisalman:ua-parser-js',
- version: '2.0.0-alpha.2',
+ version: '2.0.0-alpha.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 ea8379f8c..633425cdf 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"title": "UAParser.js",
"name": "ua-parser-js",
- "version": "2.0.0-alpha.2",
+ "version": "2.0.0-alpha.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": [
diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js
index 6d88e813f..17a518a69 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-alpha.2
+/* Enums for UAParser.js v2.0.0-alpha.3
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs
index d766e247f..6479043bc 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-alpha.2
+/* Enums for UAParser.js v2.0.0-alpha.3
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js
index 3ace7c1bb..aa3895bd4 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-alpha.2
+/* Extensions for UAParser.js v2.0.0-alpha.3
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs
index d0b6f8cdc..97e07f183 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-alpha.2
+/* Extensions for UAParser.js v2.0.0-alpha.3
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
@@ -28,109 +28,225 @@ const Apps = Object.freeze({
const Bots = Object.freeze({
browser : [
// Googlebot / BingBot / MSNBot / FacebookBot
- [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
+ [/((?: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']]
]
});
+const CLIs = Object.freeze({
+ browser : [
+ // wget / curl / lynx
+ [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
+ ]
+});
+
const ExtraDevices = Object.freeze({
- device : [
- [
- /(nook)[\w ]+build\/(\w+)/i, // Nook
- /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
- /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
- /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
- /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
- /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
+ device : [[
+ /(nook)[\w ]+build\/(\w+)/i, // Nook
+ /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
+ /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
+ /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
+ /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
+ /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
+ ], [VENDOR, MODEL, [TYPE, TABLET]], [
- /(u304aa)/i // AT&T
- ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
+ /(u304aa)/i // AT&T
+ ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
- /\bsie-(\w*)/i // Siemens
- ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
+ /\bsie-(\w*)/i // Siemens
+ ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
- /\b(rct\w+) b/i // RCA Tablets
- ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
+ /\b(rct\w+) b/i // RCA Tablets
+ ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
- /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
- ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
+ /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
+ ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
- /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
- ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
+ /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
+ ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
- /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
- ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
+ /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
+ ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
- /\b(tm\d{3}\w+) b/i
- ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
+ /\b(tm\d{3}\w+) b/i
+ ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
- /\b(k88) b/i // ZTE K Series Tablet
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
+ /\b(k88) b/i // ZTE K Series Tablet
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
- /\b(nx\d{3}j) b/i // ZTE Nubia
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
+ /\b(nx\d{3}j) b/i // ZTE Nubia
+ ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
- /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
+ /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
- /\b(zur\d{3}) b/i // Swiss ZUR Tablet
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
+ /\b(zur\d{3}) b/i // Swiss ZUR Tablet
+ ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
- /\b((zeki)?tb.*\b) b/i // Zeki Tablets
- ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
+ /\b((zeki)?tb.*\b) b/i // Zeki Tablets
+ ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
- /\b([yr]\d{2}) b/i,
- /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
- ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [
+ /\b([yr]\d{2}) b/i,
+ /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
+ ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [
- /\b(ns-?\w{0,9}) b/i // Insignia Tablets
- ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
+ /\b(ns-?\w{0,9}) b/i // Insignia Tablets
+ ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
- /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
- ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
+ /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
+ ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
- /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
- ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
+ /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
+ ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
- /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
- ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
+ /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
+ ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
- /\b(ph-1) /i // Essential PH-1
- ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
+ /\b(ph-1) /i // Essential PH-1
+ ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
- /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
- ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
+ /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
+ ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
- /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
- ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
+ /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
+ ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
- /\btu_(1491) b/i // Rotor Tablets
- ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]
- ]
+ /\btu_(1491) b/i // Rotor Tablets
+ ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]]
]
});
const Emails = Object.freeze({
browser : [
- // Microsoft Outlook / Thunderbird
+ // Microsoft Outlook / Thunderbird
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
]
});
-const CLI = Object.freeze({
+const MediaPlayers = Object.freeze({
+ browser : [[
+
+ /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia
+ /(coremedia) v([\w\._]+)/i
+ ], [NAME, VERSION], [
+
+ /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer
+ ], [NAME, VERSION], [
+
+ /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy
+ ], [NAME, VERSION], [
+
+ /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i,
+ // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC
+ // NSPlayer/PSP-InternetRadioPlayer/Videos
+ /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD
+ /(lg player|nexplayer)\s([\d\.]+)/i,
+ /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player
+ ], [NAME, VERSION], [
+ /(nexplayer)\s([\w\.-]+)/i // Nexplayer
+ ], [NAME, VERSION], [
+
+ /(flrp)\/([\w\.-]+)/i // Flip Player
+ ], [[NAME, 'Flip Player'], VERSION], [
+
+ /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i
+ // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit
+ ], [NAME], [
+
+ /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i
+ // Gstreamer
+ ], [NAME, VERSION], [
+
+ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player
+ /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i,
+ // Java/urllib/requests/wget/cURL
+ /(lavf)([\d\.]+)/i // Lavf (FFMPEG)
+ ], [NAME, VERSION], [
+
+ /(htc_one_s)\/([\d\.]+)/i, // HTC One S
+ ], [[NAME, /_/g, ' '], VERSION], [
+
+ /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i,
+ // MPlayer SVN
+ ], [NAME, VERSION], [
+
+ /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer
+ /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN
+ ], [NAME, VERSION], [
+
+ /(mplayer)/i, // MPlayer (no other info)
+ /(yourmuze)/i, // YourMuze
+ /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime
+ ], [NAME], [
+
+ /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout
+ ], [NAME, VERSION], [
+
+ /(nokia\d+)\/([\w\.-]+)/i // Nokia
+ ], [NAME, VERSION], [
+
+ /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird
+ ], [NAME, VERSION], [
+
+ /(winamp)3 version ([\w\.-]+)/i, // Winamp
+ /(winamp)\s([\w\.-]+)/i,
+ /(winamp)mpeg\/([\w\.-]+)/i
+ ], [NAME, VERSION], [
+
+ /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info)
+ // inlight radio
+ ], [NAME], [
+
+ /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i
+ // QuickTime/RealMedia/RadioApp/RadioClientApplication/
+ // SoundTap/Totem/Stagefright/Streamium
+ ], [NAME, VERSION], [
+
+ /(smp)([\d\.]+)/i // SMP
+ ], [NAME, VERSION], [
+
+ /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan
+ /(vlc)\/([\w\.-]+)/i,
+ /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp
+ /(foobar2000)\/([\d\.]+)/i, // Foobar2000
+ /(itunes)\/([\d\.]+)/i // iTunes
+ ], [NAME, VERSION], [
+
+ /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player
+ /(windows-media-player)\/([\w\.-]+)/i
+ ], [[NAME, /-/g, ' '], VERSION], [
+
+ /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i,
+ // Windows Media Server
+ ], [VERSION, [NAME, 'Windows']], [
+
+ /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm
+ ], [NAME, VERSION], [
+
+ /(rad.io)\s([\d\.]+)/i, // Rad.io
+ /(radio.(?:de|at|fr))\s([\d\.]+)/i
+ ], [[NAME, 'rad.io'], VERSION]
+ ]
+});
+
+const Modules = Object.freeze({
browser : [
- // wget / curl / lynx
- [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
+ // Axios/jsdom/Scrapy
+ [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']]
]
});
export {
Apps,
Bots,
+ CLIs,
ExtraDevices,
Emails,
- CLI
+ MediaPlayers,
+ Modules
};
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js
index 9e6c3395a..85e830b73 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-alpha.2
+/* Helpers for UAParser.js v2.0.0-alpha.3
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs
new file mode 100644
index 000000000..37ec40122
--- /dev/null
+++ b/src/helpers/ua-parser-helpers.mjs
@@ -0,0 +1,46 @@
+// Generated ESM version of UAParser.js helpers
+// DO NOT EDIT THIS FILE!
+// Source: /src/helpers/ua-parser-helpers.js
+
+///////////////////////////////////////////////
+/* Helpers for UAParser.js v2.0.0-alpha.3
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+//////////////////////////////////////////////
+
+/*jshint esversion: 6 */
+
+/*
+ # Reference:
+ https://www.chromium.org/updates/ua-reduction/
+
+ # Desktop
+ ---
+ Format:
+ Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
+
+ Possible values:
+ - Windows NT 10.0; Win64; x64
+ - Macintosh; Intel Mac OS X 10_15_7
+ - X11; Linux x86_64
+ - X11; CrOS x86_64 14541.0.0
+ - Fuchsia
+
+ # Mobile & Tablet: (except iOS/Android WebView)
+ ---
+ Format:
+ Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
+
+ Possible values:
+ - "Mobile"
+ - "" (empty string for Tablets & Desktop)
+*/
+
+const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/;
+
+const isFrozenUA = str => frozenUA.test(str);
+
+export {
+ isFrozenUA
+};
\ No newline at end of file
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index 861bb105b..793b248c1 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////////////
-/* UAParser.js v2.0.0-alpha.2
+/* UAParser.js v2.0.0-alpha.3
Copyright © 2012-2023 Faisal Salman
MIT License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -20,7 +20,7 @@
/////////////
- var LIBVERSION = '2.0.0-alpha.2',
+ var LIBVERSION = '2.0.0-alpha.3',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -50,12 +50,12 @@
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_MOBILE = CH_HEADER + '-mobile',
- CH_HEADER_MODEL = CH_HEADER + '-model',
- CH_HEADER_PLATFORM = CH_HEADER + '-platform',
+ CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
+ 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', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
+ CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS],
UA_BROWSER = 'browser',
UA_CPU = 'cpu',
UA_DEVICE = 'device',
diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs
index e49eb7278..6f716d620 100644
--- a/src/main/ua-parser.mjs
+++ b/src/main/ua-parser.mjs
@@ -3,7 +3,7 @@
// Source: /src/main/ua-parser.js
/////////////////////////////////////////////////////////////////////////////////
-/* UAParser.js v2.0.0-alpha.2
+/* UAParser.js v2.0.0-alpha.3
Copyright © 2012-2023 Faisal Salman
MIT License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -22,7 +22,7 @@
/////////////
- var LIBVERSION = '2.0.0-alpha.2',
+ var LIBVERSION = '2.0.0-alpha.3',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -52,12 +52,12 @@
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_MOBILE = CH_HEADER + '-mobile',
- CH_HEADER_MODEL = CH_HEADER + '-model',
- CH_HEADER_PLATFORM = CH_HEADER + '-platform',
+ CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
+ 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', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
+ CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS],
UA_BROWSER = 'browser',
UA_CPU = 'cpu',
UA_DEVICE = 'device',
@@ -349,7 +349,7 @@
/(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
/safari (line)\/([\w\.]+)/i, // Line App for iOS
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
- /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
+ /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
], [NAME, VERSION], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
@@ -476,7 +476,7 @@
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
// Xiaomi
- /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO
+ /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
/\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
@@ -584,7 +584,7 @@
], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
// MIXED
- /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
+ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
/(hp) ([\w ]+\w)/i, // HP iPAQ
/(asus)-?(\w+)/i, // Asus
@@ -629,7 +629,7 @@
], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
/crkey/i // Google Chromecast
], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
- /droid.+aft(\w)( bui|\))/i // Fire TV
+ /droid.+aft(\w+)( bui|\))/i // Fire TV
], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
/\(dtv[\);].+(aquos)/i,
/(aquos-tv[\w ]+)\)/i // Sharp
@@ -927,7 +927,6 @@
setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
- [BRANDS, itemListToArray(uach[CH_HEADER])],
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
@@ -1149,7 +1148,8 @@
['getResult', createItemFunc(UA_RESULT)],
['getUA', function () { return userAgent; }],
['setUA', function (ua) {
- userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
+ if (typeof ua === STR_TYPE)
+ userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
return this;
}]
])
From 2046b77ede0f8cb39971e948792af84d272fd5dc Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 20 Aug 2023 13:41:03 +0700
Subject: [PATCH 113/388] Improve `device.type` detection using client hints
"form-factor" data
---
src/main/ua-parser.js | 15 +++++++++++++++
test/mocha-test.js | 19 +++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index 793b248c1..7047e8327 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -43,6 +43,7 @@
USER_AGENT = 'user-agent',
UA_MAX_LENGTH = 350,
BRANDS = 'brands',
+ FORMFACTOR = 'formFactor',
FULLVERLIST = 'fullVersionList',
PLATFORM = 'platform',
PLATFORMVER = 'platformVersion',
@@ -51,6 +52,7 @@
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,
@@ -256,6 +258,15 @@
'8.1' : 'NT 6.3',
'10' : ['NT 6.4', 'NT 10.0'],
'RT' : 'ARM'
+ },
+
+ formFactorMap = {
+ 'embedded' : 'Automotive',
+ 'mobile' : 'Mobile',
+ 'tablet' : 'Tablet',
+ 'smarttv' : 'TV',
+ 'wearable' : ['VR', 'XR'],
+ '?' : 'Unknown'
};
//////////////
@@ -930,6 +941,7 @@
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
+ [FORMFACTOR, stripQuotes(uach[CH_HEADER_FORM_FACTOR])],
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
]);
} else {
@@ -1044,6 +1056,9 @@
if (uaCH[MODEL]) {
this.set(MODEL, uaCH[MODEL]);
}
+ if (uaCH[FORMFACTOR]) {
+ this.set(TYPE, strMapper(uaCH[FORMFACTOR], formFactorMap));
+ }
break;
case UA_OS:
var osName = uaCH[PLATFORM];
diff --git a/test/mocha-test.js b/test/mocha-test.js
index 682f710e0..e1db526ab 100644
--- a/test/mocha-test.js
+++ b/test/mocha-test.js
@@ -462,4 +462,23 @@ describe('Map UA-CH headers', function () {
assert.strictEqual(ua.device.is("tablet"), false);
});
});
+
+ it('Can detect form-factor from client-hints', function () {
+
+ const FFVR = {
+ 'sec-ch-ua-form-factor' : 'VR'
+ };
+
+ const FFUnknown = {
+ 'sec-ch-ua-form-factor' : 'Unknown'
+ };
+
+ UAParser(FFVR).withClientHints().then(function (ua) {
+ assert.strictEqual(ua.device.type, 'wearable');
+ });
+
+ UAParser(FFUnknown).withClientHints().then(function (ua) {
+ assert.strictEqual(ua.device.type, undefined);
+ });
+ });
});
\ No newline at end of file
From 594806072930fa88966e02640f81b775fe57e71d Mon Sep 17 00:00:00 2001
From: Andreas Kogler
Date: Sun, 20 Aug 2023 09:25:56 +0200
Subject: [PATCH 114/388] Implement PlayStation app `WebMAF` detection (#649)
* Add support for `WebMAF` detection on Playstation
* Extend `WebMAF` support for PlayStation 5
* Update changelog
---
changelog.md | 1 +
src/main/ua-parser.js | 1 +
test/specs/browser-all.json | 20 ++++++++++++++++++++
3 files changed, 22 insertions(+)
diff --git a/changelog.md b/changelog.md
index ac8e493be..3baed6c59 100644
--- a/changelog.md
+++ b/changelog.md
@@ -4,6 +4,7 @@
- What's breaking:
- Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"`
- OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"`
+ - Apps on PlayStation 4/5 now report `WebMAF` instead of `WebKit` for `browser.name` and the `WebMAF`-version in `browser.version`
- What's new:
- Add some new methods in result object:
- Add support for client hints: `withClientHints()`
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index a8ed359e7..c2b736d4a 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -285,6 +285,7 @@
// Mixed
/(kindle)\/([\w\.]+)/i, // Kindle
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
+ /(webmaf)\/([a-z0-9\.-]+)/i, // Sony/Playstation WebMAF
// Trident based
/(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
/(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser
diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json
index 9ca190c61..d876d5508 100644
--- a/test/specs/browser-all.json
+++ b/test/specs/browser-all.json
@@ -1148,6 +1148,26 @@
"major" : "undefined"
}
},
+ {
+ "desc" : "Sony WebMAF SDK (Playstation)",
+ "ua" : "Mozilla/5.0 (PlayStation 4 WebMAF) AppleWebKit/601.2 (KHTML, like Gecko) WebMAF/v3.1.0-0-ge5873ba4 SDK: (0x09508001u), Built: Nov 1 2022 14:36:14",
+ "expect" :
+ {
+ "name" : "WebMAF",
+ "version" : "v3.1.0-0-ge5873ba4",
+ "major" : "3"
+ }
+ },
+ {
+ "desc" : "Sony WebMAF SDK (Playstation)",
+ "ua" : "Mozilla/5.0 (PlayStation 5; WebMAF/1.0.0) AppleWebKit/537.73 (KHTML, like Gecko)",
+ "expect" :
+ {
+ "name" : "WebMAF",
+ "version" : "1.0.0",
+ "major" : "1"
+ }
+ },
{
"desc" : "Swiftfox",
"ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 (Swiftfox)",
From 73a936001a96e8d1f6be847ec169b6574f7664c4 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 21 Aug 2023 11:28:30 +0700
Subject: [PATCH 115/388] Modify `ua-parser-js/helpers` submodule into
`@ua-parser-js/helpers` scoped package
---
package-lock.json | 12 ++++++++++++
package.json | 7 +++----
src/helpers/package.json | 29 +++++++++++++++++++++++++++++
src/helpers/ua-parser-helpers.js | 6 ++----
src/helpers/ua-parser-helpers.mjs | 6 ++----
5 files changed, 48 insertions(+), 12 deletions(-)
create mode 100644 src/helpers/package.json
diff --git a/package-lock.json b/package-lock.json
index f0d514f4f..9d1ca35ff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,9 @@
}
],
"license": "MIT",
+ "workspaces": [
+ "src/helpers"
+ ],
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
@@ -760,6 +763,10 @@
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"dev": true
},
+ "node_modules/@ua-parser-js/helpers": {
+ "resolved": "src/helpers",
+ "link": true
+ },
"node_modules/@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
@@ -3770,6 +3777,11 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "src/helpers": {
+ "name": "@ua-parser-js/helpers",
+ "version": "2.0.0-alpha.3",
+ "license": "MIT"
}
}
}
diff --git a/package.json b/package.json
index 633425cdf..205980ee7 100644
--- a/package.json
+++ b/package.json
@@ -157,10 +157,6 @@
"./extensions": {
"require": "./src/extensions/ua-parser-extensions.js",
"import": "./src/extensions/ua-parser-extensions.mjs"
- },
- "./helpers": {
- "require": "./src/helpers/ua-parser-helpers.js",
- "import": "./src/helpers/ua-parser-helpers.mjs"
}
},
"files": [
@@ -217,5 +213,8 @@
"type": "github",
"url": "https://github.com/sponsors/faisalman"
}
+ ],
+ "workspaces": [
+ "src/helpers"
]
}
diff --git a/src/helpers/package.json b/src/helpers/package.json
new file mode 100644
index 000000000..fe99fb285
--- /dev/null
+++ b/src/helpers/package.json
@@ -0,0 +1,29 @@
+{
+ "title": "UAParser.js Helpers",
+ "name": "@ua-parser-js/helpers",
+ "version": "2.0.0-alpha.3",
+ "author": "Faisal Salman ",
+ "description": "Helpers for UAParser.js",
+ "main": "ua-parser-helpers.js",
+ "module": "ua-parser-helpers.mjs",
+ "scripts" : {
+ "test": "echo 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/faisalman/ua-parser-js.git"
+ },
+ "keywords": [
+ "ua-parser-js",
+ "browser-detection",
+ "device-detection",
+ "os-detection",
+ "user-agent",
+ "client-hints"
+ ],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/faisalman/ua-parser-js/issues"
+ },
+ "homepage": "https://github.com/faisalman/ua-parser-js#readme"
+}
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js
index 85e830b73..eaa317143 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-alpha.3
+/* Helpers for UAParser.js
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
@@ -33,9 +33,7 @@
- "" (empty string for Tablets & Desktop)
*/
-const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/;
-
-const isFrozenUA = str => frozenUA.test(str);
+const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua);
module.exports = {
isFrozenUA
diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs
index 37ec40122..bf0dd6650 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-alpha.3
+/* Helpers for UAParser.js
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
@@ -37,9 +37,7 @@
- "" (empty string for Tablets & Desktop)
*/
-const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/;
-
-const isFrozenUA = str => frozenUA.test(str);
+const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua);
export {
isFrozenUA
From 3dd4b60ee97ad1e9485a5dbeef1c9f136e3fb9a3 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 22 Aug 2023 01:09:35 +0700
Subject: [PATCH 116/388] [helpers] Add new method: `unfreezeUA()`, construct
new unfreezed user-agent string using real data from client hints
---
src/helpers/package.json | 4 +-
src/helpers/readme.md | 65 +++++++++++++++++++++++++++
src/helpers/ua-parser-helpers.d.ts | 2 +
src/helpers/ua-parser-helpers.js | 63 ++++++++++++++++++++++++--
test/mocha-test-helpers.js | 2 +-
test/playwright-test-helpers.spec.mjs | 47 +++++++++++++++++++
6 files changed, 176 insertions(+), 7 deletions(-)
create mode 100644 src/helpers/readme.md
create mode 100644 src/helpers/ua-parser-helpers.d.ts
create mode 100644 test/playwright-test-helpers.spec.mjs
diff --git a/src/helpers/package.json b/src/helpers/package.json
index fe99fb285..092ffc956 100644
--- a/src/helpers/package.json
+++ b/src/helpers/package.json
@@ -1,9 +1,9 @@
{
"title": "UAParser.js Helpers",
"name": "@ua-parser-js/helpers",
- "version": "2.0.0-alpha.3",
+ "version": "0.0.1",
"author": "Faisal Salman ",
- "description": "Helpers for UAParser.js",
+ "description": "A collection of utility methods for UAParser.js",
"main": "ua-parser-helpers.js",
"module": "ua-parser-helpers.mjs",
"scripts" : {
diff --git a/src/helpers/readme.md b/src/helpers/readme.md
new file mode 100644
index 000000000..e23abc2e7
--- /dev/null
+++ b/src/helpers/readme.md
@@ -0,0 +1,65 @@
+# @ua-parser-js/helpers
+
+This package contains a collection of utility methods for [UAParser.js](https://github.com/faisalman/ua-parser-js)
+
+```sh
+npm i @ua-parser-js/helpers
+```
+
+### * `isFrozenUA(ua:string):boolean`
+
+Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/)
+
+### * `unfreezeUA():Promise`
+
+construct new unfreezed user-agent string using real data from client hints
+
+## Code Example
+
+```js
+import { isFrozenUA } from '@ua-parser-js/helpers';
+
+const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
+const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
+
+console.log(isFrozenUA(regularMobileUA));
+// false
+
+console.log(isFrozenUA(freezedMobileUA));
+// true
+```
+
+```js
+import { unfreezeUA } from '@ua-parser-js/helpers';
+
+/*
+ Suppose we're in a browser having this client hints data:
+
+ {
+ fullVersionList: [
+ {
+ brand: 'New Browser',
+ version: '110.1.2.3'
+ },
+ {
+ brand: 'Chromium',
+ version: '110.1.2.3'
+ },
+ {
+ brand: 'Not(A:Brand',
+ version: '110'
+ }
+ ],
+ platform: 'Windows',
+ platformVersion: '13.0.0'
+ }
+
+ And a freezed user-agent:
+
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
+*/
+
+unfreezeUA();
+
+// 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
+```
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts
new file mode 100644
index 000000000..61e041f48
--- /dev/null
+++ b/src/helpers/ua-parser-helpers.d.ts
@@ -0,0 +1,2 @@
+export function isFrozenUA(ua: string): boolean;
+export function unfreezeUA(): Promise;
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js
index eaa317143..45933eb90 100644
--- a/src/helpers/ua-parser-helpers.js
+++ b/src/helpers/ua-parser-helpers.js
@@ -1,11 +1,11 @@
///////////////////////////////////////////////
-/* Helpers for UAParser.js
+/* A collection of utility methods for UAParser.js
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
//////////////////////////////////////////////
-/*jshint esversion: 6 */
+/*jshint esversion: 11 */
/*
# Reference:
@@ -33,8 +33,63 @@
- "" (empty string for Tablets & Desktop)
*/
-const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua);
+const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua);
+
+const unfreezeUA = async () => {
+ if (!navigator) {
+ throw new Error('Currently only support browser environment');
+ } else {
+ let ua = navigator.userAgent;
+ if (navigator.userAgentData && isFrozenUA(ua)) {
+ const ch = await navigator.userAgentData.getHighEntropyValues(['architecture', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
+ switch (ch.platform) {
+ case 'Windows':
+ let [major, minor] = ch.platformVersion?.split('.').map(i => parseInt(i, 10));
+ let osReplacer = (major < 1) ?
+ `$ 6.${minor}` :
+ (major >= 13) ?
+ `$ 11.${minor}` :
+ `$ 10.${minor}`;
+ let archReplacer = (ch.architecture == 'arm') ?
+ '; ARM' :
+ (ch.wow64) ?
+ '; WOW64' :
+ (ch.architecture == 'x86' && ch.bitness == '64') ?
+ '; $' : '';
+ ua = ua.replace(/(?Windows NT) 10\.0/, osReplacer)
+ .replace(/; (?Win64; x64)/, archReplacer);
+ break;
+ case 'Android':
+ ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`);
+ break;
+ case 'Linux':
+ case 'Chrome OS':
+ archReplacer = (ch.architecture == 'arm') ?
+ ((ch.bitness == '64') ? 'arm64' : 'arm') :
+ (ch.architecture == 'x86' && ch.bitness == '64') ?
+ '$' : 'x86';
+
+ ua = ua.replace(/(?x86_64)/, archReplacer);
+ break;
+ case 'macOS':
+ ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`);
+ break;
+ }
+ let browserReplacer = '';
+ ch.fullVersionList?.forEach((browser) => {
+ if (!/not.a.brand/i.test(browser.brand)) {
+ browserReplacer += `${browser.brand}/${browser.version} `;
+ }
+ });
+ if (browserReplacer) {
+ ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, browserReplacer);
+ }
+ }
+ return ua;
+ }
+};
module.exports = {
- isFrozenUA
+ isFrozenUA,
+ unfreezeUA
};
\ No newline at end of file
diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js
index b954e73cc..c8417d629 100644
--- a/test/mocha-test-helpers.js
+++ b/test/mocha-test-helpers.js
@@ -1,4 +1,4 @@
-const { isFrozenUA } = require('ua-parser-js/helpers');
+const { isFrozenUA } = require('@ua-parser-js/helpers');
const assert = require('assert');
describe('isFrozenUA', () => {
diff --git a/test/playwright-test-helpers.spec.mjs b/test/playwright-test-helpers.spec.mjs
new file mode 100644
index 000000000..c5bc65e70
--- /dev/null
+++ b/test/playwright-test-helpers.spec.mjs
@@ -0,0 +1,47 @@
+// @ts-check
+import { test, expect } from '@playwright/test';
+import { unfreezeUA } from '@ua-parser-js/helpers';
+
+test('test for unfreezeUA() method', async ({ page }) => {
+
+
+ await page.addInitScript(() => {
+ Object.defineProperty(navigator, 'userAgentData', {
+ value: {
+ brands: [],
+ platform: 'Windows',
+ mobile: false,
+ getHighEntropyValues: () => {
+ return Promise.resolve({
+ architecture: 'x86',
+ bitness: '64',
+ fullVersionList: [
+ {
+ brand: 'New Browser',
+ version: '110.1.2.3'
+ },
+ {
+ brand: 'Chromium',
+ version: '110.1.2.3'
+ },
+ {
+ brand: 'Not(A:Brand',
+ version: '110'
+ }
+ ],
+ platform: 'Windows',
+ platformVersion: '0.3'
+ });
+ }
+ }
+ });
+ Object.defineProperty(navigator, 'userAgent', {
+ value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
+ });
+ });
+ await page.goto('about:blank');
+ await page.addScriptTag({ path: './src/helpers/ua-parser-helpers.js' });
+ // @ts-ignore
+ const ua = await page.evaluate(async () => await unfreezeUA());
+ expect(ua).toBe('Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36');
+});
\ No newline at end of file
From 129657673bec3e59a5e0be34d6ba15e47cc5e716 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Tue, 22 Aug 2023 23:39:34 +0700
Subject: [PATCH 117/388] [helpers] Add new method: `UACHParser()`, parse
client-hints HTTP headers into its JS API equivalent
---
src/helpers/package.json | 6 +-
src/helpers/readme.md | 72 +++++++++++-
src/helpers/ua-parser-helpers.d.ts | 37 +++++-
src/helpers/ua-parser-helpers.js | 177 +++++++++++++++++++++--------
test/mocha-test-helpers.js | 64 ++++++++++-
5 files changed, 297 insertions(+), 59 deletions(-)
diff --git a/src/helpers/package.json b/src/helpers/package.json
index 092ffc956..b5167cfaa 100644
--- a/src/helpers/package.json
+++ b/src/helpers/package.json
@@ -1,12 +1,12 @@
{
"title": "UAParser.js Helpers",
"name": "@ua-parser-js/helpers",
- "version": "0.0.1",
+ "version": "0.0.3",
"author": "Faisal Salman ",
"description": "A collection of utility methods for UAParser.js",
"main": "ua-parser-helpers.js",
"module": "ua-parser-helpers.mjs",
- "scripts" : {
+ "scripts": {
"test": "echo 1"
},
"repository": {
@@ -26,4 +26,4 @@
"url": "https://github.com/faisalman/ua-parser-js/issues"
},
"homepage": "https://github.com/faisalman/ua-parser-js#readme"
-}
\ No newline at end of file
+}
diff --git a/src/helpers/readme.md b/src/helpers/readme.md
index e23abc2e7..5f1efd2c8 100644
--- a/src/helpers/readme.md
+++ b/src/helpers/readme.md
@@ -12,7 +12,11 @@ Check whether a user-agent string match with [frozen user-agent pattern](https:/
### * `unfreezeUA():Promise`
-construct new unfreezed user-agent string using real data from client hints
+Construct new unfreezed user-agent string using real data from client hints
+
+### * `UACHParser(headers: object): object`
+
+Parse client hints HTTP headers (sec-ch-ua) into its JS API equivalent
## Code Example
@@ -59,7 +63,69 @@ import { unfreezeUA } from '@ua-parser-js/helpers';
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
*/
-unfreezeUA();
-
+unfreezeUA()
+ .then(ua => console.log(ua));
// 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
+```
+
+```js
+import { UACHParser } from '@ua-parser-js/helpers';
+
+/*
+ Suppose we're in a server having this client hints data:
+
+ const headers = {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
+ 'sec-ch-ua-arch' : 'arm',
+ 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Pixel 99',
+ 'sec-ch-ua-platform' : 'Linux',
+ 'sec-ch-ua-platform-version' : '13',
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+ };
+*/
+
+const userAgentData = UACHParser(headers);
+
+console.log(userAgentData);
+/*
+ {
+ "architecture": "arm",
+ "bitness": "64",
+ "brands": [
+ {
+ "brand": "Chromium",
+ "version": "93"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99"
+ }
+ ],
+ "fullVersionList": [
+ {
+ "brand": "Chromium",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99.0.1.2"
+ }
+ ],
+ "mobile": true,
+ "model": "Pixel 99",
+ "platform": "Linux",
+ "platformVersion": "13"
+ }
+*/
```
\ 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 61e041f48..836de09d4 100644
--- a/src/helpers/ua-parser-helpers.d.ts
+++ b/src/helpers/ua-parser-helpers.d.ts
@@ -1,2 +1,37 @@
+interface IBrowser {
+ brand: string;
+ version: string;
+}
+
+interface ClientHintsJSLowEntropy {
+ brands: Array;
+ mobile: boolean;
+ platform: string;
+}
+
+export interface ClientHintsJSHighEntropy extends ClientHintsJSLowEntropy {
+ architecture?: string;
+ bitness?: string;
+ formFactor?: string;
+ fullVersionList?: Array;
+ model?: string;
+ platformVersion?: string;
+ wow64?: boolean;
+};
+
+export interface ClientHintsHTTPHeaders {
+ 'sec-ch-ua-arch'?: string;
+ 'sec-ch-ua-bitness'?: string;
+ 'sec-ch-ua'?: string;
+ 'sec-ch-ua-form-factor'?: string;
+ 'sec-ch-ua-full-version-list'?: string;
+ 'sec-ch-ua-mobile'?: string;
+ 'sec-ch-ua-model'?: string;
+ 'sec-ch-ua-platform'?: string;
+ 'sec-ch-ua-platform-version'?: string;
+ 'sec-ch-ua-wow64'?: string;
+}
+
export function isFrozenUA(ua: string): boolean;
-export function unfreezeUA(): Promise;
\ No newline at end of file
+export function unfreezeUA(): Promise;
+export function UACHParser(headers: ClientHintsHTTPHeaders): ClientHintsJSHighEntropy;
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js
index 45933eb90..26d28f726 100644
--- a/src/helpers/ua-parser-helpers.js
+++ b/src/helpers/ua-parser-helpers.js
@@ -35,61 +35,138 @@
const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua);
-const unfreezeUA = async () => {
- if (!navigator) {
- throw new Error('Currently only support browser environment');
+const unfreezeUA = async (ua, ch) => {
+ const env = typeof navigator == 'undefined' ? 'node' : 'browser';
+ if (env == 'node') {
+ if (!ua['user-agent']) {
+ throw new Error('User-Agent header not found');
+ }
+ ch = UACHParser(ua);
+ ua = ua['user-agent'];
} else {
- let ua = navigator.userAgent;
- if (navigator.userAgentData && isFrozenUA(ua)) {
- const ch = await navigator.userAgentData.getHighEntropyValues(['architecture', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
- switch (ch.platform) {
- case 'Windows':
- let [major, minor] = ch.platformVersion?.split('.').map(i => parseInt(i, 10));
- let osReplacer = (major < 1) ?
- `$ 6.${minor}` :
- (major >= 13) ?
- `$ 11.${minor}` :
- `$ 10.${minor}`;
- let archReplacer = (ch.architecture == 'arm') ?
- '; ARM' :
- (ch.wow64) ?
- '; WOW64' :
- (ch.architecture == 'x86' && ch.bitness == '64') ?
- '; $' : '';
- ua = ua.replace(/(?Windows NT) 10\.0/, osReplacer)
- .replace(/; (?Win64; x64)/, archReplacer);
- break;
- case 'Android':
- ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`);
- break;
- case 'Linux':
- case 'Chrome OS':
- archReplacer = (ch.architecture == 'arm') ?
- ((ch.bitness == '64') ? 'arm64' : 'arm') :
- (ch.architecture == 'x86' && ch.bitness == '64') ?
- '$' : 'x86';
-
- ua = ua.replace(/(?x86_64)/, archReplacer);
- break;
- case 'macOS':
- ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`);
- break;
- }
- let browserReplacer = '';
- ch.fullVersionList?.forEach((browser) => {
- if (!/not.a.brand/i.test(browser.brand)) {
- browserReplacer += `${browser.brand}/${browser.version} `;
- }
- });
- if (browserReplacer) {
- ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, browserReplacer);
- }
+ ua = ua || navigator.userAgent;
+ ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
+ }
+ if (isFrozenUA(ua) && ch) {
+ switch (ch.platform) {
+ case 'Windows':
+ let [major, minor] = ch.platformVersion
+ .split('.')
+ .map(num => parseInt(num, 10));
+ major = (major < 1) ? '6' : (major >= 13) ? '11' : '10';
+ ua = ua .replace(/(?Windows NT) 10\.0/, `$ ${major}.${minor}`)
+ .replace(/; (?Win64; x64)/,
+ (ch.architecture == 'arm') ?
+ '; ARM' :
+ (ch.wow64) ?
+ '; WOW64' :
+ (ch.architecture == 'x86' && ch.bitness != '64') ?
+ '' : '; $');
+ break;
+ case 'Android':
+ ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`);
+ break;
+ case 'Linux':
+ case 'Chrome OS':
+ ua = ua.replace(/(?x86_64)/,
+ (ch.architecture == 'arm') ?
+ ((ch.bitness == '64') ? 'arm64' : 'arm') :
+ (ch.architecture == 'x86' && ch.bitness != '64') ?
+ 'x86' : '$');
+ break;
+ case 'macOS':
+ ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`);
+ break;
+ }
+ if (ch.fullVersionList) {
+ ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /,
+ ch.fullVersionList
+ .filter(browser => !/not.a.brand/i.test(browser.brand))
+ .map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `)
+ .join(''));
}
- return ua;
}
+ return ua;
+};
+
+const UACHMap = {
+ 'sec-ch-ua-arch' : {
+ prop : 'architecture',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-bitness' : {
+ prop : 'bitness',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua' : {
+ prop : 'brands',
+ type : 'sf-list'
+ },
+ 'sec-ch-ua-form-factor' : {
+ prop : 'formFactor',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-full-version-list' : {
+ prop : 'fullVersionList',
+ type : 'sf-list'
+ },
+ 'sec-ch-ua-mobile' : {
+ prop : 'mobile',
+ type : 'sf-boolean',
+ },
+ 'sec-ch-ua-model' : {
+ prop : 'model',
+ type : 'sf-string',
+ },
+ 'sec-ch-ua-platform' : {
+ prop : 'platform',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-platform-version' : {
+ prop : 'platformVersion',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-wow64' : {
+ prop : 'wow64',
+ type : 'sf-boolean'
+ }
+};
+
+const UACHParser = (headers) => {
+ const parse = (str, type) => {
+ if (!str) {
+ return '';
+ }
+ switch (type) {
+ case 'sf-boolean':
+ return /\?1/.test(str);
+ case 'sf-list':
+ return str.replace(/\\?\"/g, '')
+ .split(', ')
+ .map(brands => {
+ const [brand, version] = brands.split(';v=');
+ return {
+ brand : brand,
+ version : version
+ };
+ });
+ case 'sf-string':
+ default:
+ return str.replace(/\\?\"/g, '');
+ }
+ };
+ let ch = {};
+ Object.keys(UACHMap).forEach(field => {
+ if (headers.hasOwnProperty(field)) {
+ const { prop, type } = UACHMap[field];
+ ch[prop] = parse(headers[field], type);
+ }
+ });
+ return ch;
};
module.exports = {
isFrozenUA,
- unfreezeUA
+ unfreezeUA,
+ UACHParser
};
\ No newline at end of file
diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js
index c8417d629..07ba15c6c 100644
--- a/test/mocha-test-helpers.js
+++ b/test/mocha-test-helpers.js
@@ -1,7 +1,7 @@
-const { isFrozenUA } = require('@ua-parser-js/helpers');
+const { isFrozenUA, unfreezeUA, UACHParser } = require('@ua-parser-js/helpers');
const assert = require('assert');
-describe('isFrozenUA', () => {
+describe('isFrozenUA()', () => {
it('Returns whether a user agent is frozen', () => {
const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
@@ -32,4 +32,64 @@ describe('isFrozenUA', () => {
assert.strictEqual(isFrozenUA(regularTabletUA), false);
assert.strictEqual(isFrozenUA(freezedTabletUA), true);
});
+});
+
+const headers = {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
+ 'sec-ch-ua-arch' : 'arm',
+ 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Pixel 99',
+ 'sec-ch-ua-platform' : 'Linux',
+ 'sec-ch-ua-platform-version' : '13',
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+};
+
+describe('unfreezeUA()', () => {
+ it('returns an unfreezed user-agent using real data from client hints HTTP headers (sec-ch-ua)', async () => {
+ const unfreezed = await unfreezeUA(headers);
+ assert.strictEqual(unfreezed, 'Mozilla/5.0 (X11; Linux arm64) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/93.0.1.2 Chrome/93.0.1.2 Safari/537.36');
+ });
+});
+
+describe('UACHParser()', () => {
+ it('parse client hints HTTP headers (sec-ch-ua) into a JavaScript object', () => {
+ assert.deepEqual(UACHParser(headers), {
+ "architecture": "arm",
+ "bitness": "64",
+ "brands": [
+ {
+ "brand": "Chromium",
+ "version": "93"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99"
+ }
+ ],
+ "fullVersionList": [
+ {
+ "brand": "Chromium",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99.0.1.2"
+ }
+ ],
+ "mobile": true,
+ "model": "Pixel 99",
+ "platform": "Linux",
+ "platformVersion": "13"
+ });
+ });
});
\ No newline at end of file
From 3f105fe93b8761a7d048f1d3f812c4f94bcf3c2e Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 23 Aug 2023 14:38:34 +0700
Subject: [PATCH 118/388] [helpers] split `helpers` into 2 new packages:
`user-agent-helpers` & `client-hints-helpers`
---
package-lock.json | 24 +++-
script/build-module.js | 89 ++++++------
.../client-hints-helpers.d.ts} | 2 -
.../client-hints-helpers.js | 88 ++++++++++++
.../client-hints-helpers.mjs | 92 ++++++++++++
.../package.json | 14 +-
src/client-hints-helpers/readme.md | 75 ++++++++++
src/helpers/readme.md | 131 ------------------
src/helpers/ua-parser-helpers.mjs | 44 ------
src/user-agent-helpers/package.json | 32 +++++
src/user-agent-helpers/readme.md | 67 +++++++++
.../user-agent-helpers.d.ts | 4 +
.../user-agent-helpers.js} | 87 +-----------
src/user-agent-helpers/user-agent-helpers.mjs | 101 ++++++++++++++
test/mocha-test-helpers.js | 5 +-
test/playwright-test-helpers.spec.mjs | 47 -------
16 files changed, 543 insertions(+), 359 deletions(-)
rename src/{helpers/ua-parser-helpers.d.ts => client-hints-helpers/client-hints-helpers.d.ts} (90%)
create mode 100644 src/client-hints-helpers/client-hints-helpers.js
create mode 100644 src/client-hints-helpers/client-hints-helpers.mjs
rename src/{helpers => client-hints-helpers}/package.json (62%)
create mode 100644 src/client-hints-helpers/readme.md
delete mode 100644 src/helpers/readme.md
delete mode 100644 src/helpers/ua-parser-helpers.mjs
create mode 100644 src/user-agent-helpers/package.json
create mode 100644 src/user-agent-helpers/readme.md
create mode 100644 src/user-agent-helpers/user-agent-helpers.d.ts
rename src/{helpers/ua-parser-helpers.js => user-agent-helpers/user-agent-helpers.js} (63%)
create mode 100644 src/user-agent-helpers/user-agent-helpers.mjs
delete mode 100644 test/playwright-test-helpers.spec.mjs
diff --git a/package-lock.json b/package-lock.json
index 9d1ca35ff..613ed7399 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,7 +23,8 @@
],
"license": "MIT",
"workspaces": [
- "src/helpers"
+ "src/client-hints-helpers",
+ "src/user-agent-helpers"
],
"devDependencies": {
"@babel/parser": "7.15.8",
@@ -763,8 +764,12 @@
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"dev": true
},
- "node_modules/@ua-parser-js/helpers": {
- "resolved": "src/helpers",
+ "node_modules/@ua-parser-js/client-hints-helpers": {
+ "resolved": "src/client-hints-helpers",
+ "link": true
+ },
+ "node_modules/@ua-parser-js/user-agent-helpers": {
+ "resolved": "src/user-agent-helpers",
"link": true
},
"node_modules/@ungap/promise-all-settled": {
@@ -3778,9 +3783,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "src/client-hints-helpers": {
+ "name": "@ua-parser-js/client-hints-helpers",
+ "version": "0.0.1",
+ "license": "MIT"
+ },
"src/helpers": {
"name": "@ua-parser-js/helpers",
- "version": "2.0.0-alpha.3",
+ "version": "0.0.3",
+ "extraneous": true,
+ "license": "MIT"
+ },
+ "src/user-agent-helpers": {
+ "name": "@ua-parser-js/user-agent-helpers",
+ "version": "0.0.1",
"license": "MIT"
}
}
diff --git a/script/build-module.js b/script/build-module.js
index d7f5a59ae..3b00317f6 100755
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -1,57 +1,64 @@
#!/usr/bin/env node
/* jshint esversion: 6 */
const fs = require('fs');
-const PATH = {
- main : {
- src : 'src/main/ua-parser.js',
- dest : 'src/main/ua-parser.mjs',
- title : ''
- },
- enums : {
- src : 'src/enums/ua-parser-enums.js',
- dest :'src/enums/ua-parser-enums.mjs',
- title : 'enums'
- },
- extensions : {
- src : 'src/extensions/ua-parser-extensions.js',
- dest : 'src/extensions/ua-parser-extensions.mjs',
- title : 'extensions'
- },
- helpers : {
- src : 'src/helpers/ua-parser-helpers.js',
- dest : 'src/helpers/ua-parser-helpers.mjs',
- title : 'helpers'
- }
-};
-const generateMJS = (module, replacers) => {
- const { src, dest, title } = PATH[module];
+
+const generateMJS = (module) => {
+ let { src, dest, title, replacements } = module;
let text = fs.readFileSync(src, 'utf-8');
- replacers.forEach(replacer => {
- text = text.replace(replacer[0], replacer[1]);
+
+ replacements.push(
+ [/const (.+?)\s*=\s*require\((.+)\)/ig, 'import $1 from $2'],
+ [/module\.exports =/ig, 'export']
+ );
+ replacements.forEach(rep => {
+ text = text.replace(rep[0], rep[1]);
});
console.log(`Generate ${dest}`);
fs.writeFileSync(dest,
-`// Generated ESM version of UAParser.js ${title}
+`// Generated ESM version of ${title}
// DO NOT EDIT THIS FILE!
// Source: /${src}
${text}`, 'utf-8');
-};
-
-// ua-parser.mjs
-generateMJS('main', [
- [/\(func[\s\S]+strict\';/ig, ''],
- [/esversion\: 3/ig, 'esversion: 6'],
- [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};']
-]);
-// ua-parser-enum.mjs
-generateMJS('enums', [[/module\.exports =/ig, 'export']]);
+};
-// ua-parser-extension.mjs
-generateMJS('extensions', [[/module\.exports =/ig, 'export']]);
+const modules = [
+ {
+ src : 'src/main/ua-parser.js',
+ dest : 'src/main/ua-parser.mjs',
+ title : 'ua-parser-js',
+ replacements : [
+ [/\(func[\s\S]+strict\';/ig, ''],
+ [/esversion\: 3/ig, 'esversion: 6'],
+ [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};']
+ ]
+ },{
+ src : 'src/enums/ua-parser-enums.js',
+ dest :'src/enums/ua-parser-enums.mjs',
+ title : 'ua-parser-js/enums',
+ replacements : []
+ },
+ {
+ src : 'src/extensions/ua-parser-extensions.js',
+ dest : 'src/extensions/ua-parser-extensions.mjs',
+ title : 'ua-parser-js/extensions',
+ replacements : []
+ },
+ {
+ src : 'src/user-agent-helpers/user-agent-helpers.js',
+ dest : 'src/user-agent-helpers/user-agent-helpers.mjs',
+ title : '@ua-parser-js/user-agent-helpers',
+ replacements : []
+ },
+ {
+ src : 'src/client-hints-helpers/client-hints-helpers.js',
+ dest : 'src/client-hints-helpers/client-hints-helpers.mjs',
+ title : '@ua-parser-js/client-hints-helpers',
+ replacements : []
+ }
+];
-// ua-parser-helpers.mjs
-generateMJS('helpers', [[/module\.exports =/ig, 'export']]);
\ No newline at end of file
+modules.forEach(module => generateMJS(module));
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/client-hints-helpers/client-hints-helpers.d.ts
similarity index 90%
rename from src/helpers/ua-parser-helpers.d.ts
rename to src/client-hints-helpers/client-hints-helpers.d.ts
index 836de09d4..00f98f84b 100644
--- a/src/helpers/ua-parser-helpers.d.ts
+++ b/src/client-hints-helpers/client-hints-helpers.d.ts
@@ -32,6 +32,4 @@ export interface ClientHintsHTTPHeaders {
'sec-ch-ua-wow64'?: string;
}
-export function isFrozenUA(ua: string): boolean;
-export function unfreezeUA(): Promise;
export function UACHParser(headers: ClientHintsHTTPHeaders): ClientHintsJSHighEntropy;
\ No newline at end of file
diff --git a/src/client-hints-helpers/client-hints-helpers.js b/src/client-hints-helpers/client-hints-helpers.js
new file mode 100644
index 000000000..5fa3bbc77
--- /dev/null
+++ b/src/client-hints-helpers/client-hints-helpers.js
@@ -0,0 +1,88 @@
+////////////////////////////////////////////////////
+/* A collection of utility methods for client-hints
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+///////////////////////////////////////////////////
+
+/*jshint esversion: 11 */
+
+const UACHMap = {
+ 'sec-ch-ua-arch' : {
+ prop : 'architecture',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-bitness' : {
+ prop : 'bitness',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua' : {
+ prop : 'brands',
+ type : 'sf-list'
+ },
+ 'sec-ch-ua-form-factor' : {
+ prop : 'formFactor',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-full-version-list' : {
+ prop : 'fullVersionList',
+ type : 'sf-list'
+ },
+ 'sec-ch-ua-mobile' : {
+ prop : 'mobile',
+ type : 'sf-boolean',
+ },
+ 'sec-ch-ua-model' : {
+ prop : 'model',
+ type : 'sf-string',
+ },
+ 'sec-ch-ua-platform' : {
+ prop : 'platform',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-platform-version' : {
+ prop : 'platformVersion',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-wow64' : {
+ prop : 'wow64',
+ type : 'sf-boolean'
+ }
+};
+
+const UACHParser = (headers) => {
+ const parse = (str, type) => {
+ if (!str) {
+ return '';
+ }
+ switch (type) {
+ case 'sf-boolean':
+ return /\?1/.test(str);
+ case 'sf-list':
+ return str.replace(/\\?\"/g, '')
+ .split(', ')
+ .map(brands => {
+ const [brand, version] = brands.split(';v=');
+ return {
+ brand : brand,
+ version : version
+ };
+ });
+ case 'sf-string':
+ default:
+ return str.replace(/\\?\"/g, '');
+ }
+ };
+ let ch = {};
+ Object.keys(UACHMap).forEach(field => {
+ if (headers.hasOwnProperty(field)) {
+ const { prop, type } = UACHMap[field];
+ ch[prop] = parse(headers[field], type);
+ }
+ });
+ return ch;
+};
+
+module.exports = {
+ UACHParser
+};
\ No newline at end of file
diff --git a/src/client-hints-helpers/client-hints-helpers.mjs b/src/client-hints-helpers/client-hints-helpers.mjs
new file mode 100644
index 000000000..184e0002d
--- /dev/null
+++ b/src/client-hints-helpers/client-hints-helpers.mjs
@@ -0,0 +1,92 @@
+// Generated ESM version of @ua-parser-js/client-hints-helpers
+// DO NOT EDIT THIS FILE!
+// Source: /src/client-hints-helpers/client-hints-helpers.js
+
+////////////////////////////////////////////////////
+/* A collection of utility methods for client-hints
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+///////////////////////////////////////////////////
+
+/*jshint esversion: 11 */
+
+const UACHMap = {
+ 'sec-ch-ua-arch' : {
+ prop : 'architecture',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-bitness' : {
+ prop : 'bitness',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua' : {
+ prop : 'brands',
+ type : 'sf-list'
+ },
+ 'sec-ch-ua-form-factor' : {
+ prop : 'formFactor',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-full-version-list' : {
+ prop : 'fullVersionList',
+ type : 'sf-list'
+ },
+ 'sec-ch-ua-mobile' : {
+ prop : 'mobile',
+ type : 'sf-boolean',
+ },
+ 'sec-ch-ua-model' : {
+ prop : 'model',
+ type : 'sf-string',
+ },
+ 'sec-ch-ua-platform' : {
+ prop : 'platform',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-platform-version' : {
+ prop : 'platformVersion',
+ type : 'sf-string'
+ },
+ 'sec-ch-ua-wow64' : {
+ prop : 'wow64',
+ type : 'sf-boolean'
+ }
+};
+
+const UACHParser = (headers) => {
+ const parse = (str, type) => {
+ if (!str) {
+ return '';
+ }
+ switch (type) {
+ case 'sf-boolean':
+ return /\?1/.test(str);
+ case 'sf-list':
+ return str.replace(/\\?\"/g, '')
+ .split(', ')
+ .map(brands => {
+ const [brand, version] = brands.split(';v=');
+ return {
+ brand : brand,
+ version : version
+ };
+ });
+ case 'sf-string':
+ default:
+ return str.replace(/\\?\"/g, '');
+ }
+ };
+ let ch = {};
+ Object.keys(UACHMap).forEach(field => {
+ if (headers.hasOwnProperty(field)) {
+ const { prop, type } = UACHMap[field];
+ ch[prop] = parse(headers[field], type);
+ }
+ });
+ return ch;
+};
+
+export {
+ UACHParser
+};
\ No newline at end of file
diff --git a/src/helpers/package.json b/src/client-hints-helpers/package.json
similarity index 62%
rename from src/helpers/package.json
rename to src/client-hints-helpers/package.json
index b5167cfaa..39a2dc2e6 100644
--- a/src/helpers/package.json
+++ b/src/client-hints-helpers/package.json
@@ -1,13 +1,13 @@
{
- "title": "UAParser.js Helpers",
- "name": "@ua-parser-js/helpers",
- "version": "0.0.3",
+ "title": "Client-Hints Helpers",
+ "name": "@ua-parser-js/client-hints-helpers",
+ "version": "0.0.1",
"author": "Faisal Salman ",
- "description": "A collection of utility methods for UAParser.js",
- "main": "ua-parser-helpers.js",
- "module": "ua-parser-helpers.mjs",
+ "description": "A collection of utility methods for working with client-hints",
+ "main": "client-hints-helpers.js",
+ "module": "client-hints-helpers.mjs",
"scripts": {
- "test": "echo 1"
+ "test": "mocha ../../test/mocha-test-helpers"
},
"repository": {
"type": "git",
diff --git a/src/client-hints-helpers/readme.md b/src/client-hints-helpers/readme.md
new file mode 100644
index 000000000..b1f569e38
--- /dev/null
+++ b/src/client-hints-helpers/readme.md
@@ -0,0 +1,75 @@
+# @ua-parser-js/client-hints-helpers
+
+This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client-hints.
+
+```sh
+npm i @ua-parser-js/client-hints-helpers
+```
+
+### * `UACHParser(headers:object):object`
+
+Parse user-agent client-hints HTTP headers (sec-ch-ua) into its JS API equivalent
+
+## Code Example
+
+```js
+import { UACHParser } from '@ua-parser-js/client-hints-helpers';
+
+/*
+ Suppose we're in a server having this client hints data:
+
+ const headers = {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
+ 'sec-ch-ua-arch' : 'arm',
+ 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Pixel 99',
+ 'sec-ch-ua-platform' : 'Linux',
+ 'sec-ch-ua-platform-version' : '13',
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+ };
+*/
+
+const userAgentData = UACHParser(headers);
+
+console.log(userAgentData);
+/*
+ {
+ "architecture": "arm",
+ "bitness": "64",
+ "brands": [
+ {
+ "brand": "Chromium",
+ "version": "93"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99"
+ }
+ ],
+ "fullVersionList": [
+ {
+ "brand": "Chromium",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99.0.1.2"
+ }
+ ],
+ "mobile": true,
+ "model": "Pixel 99",
+ "platform": "Linux",
+ "platformVersion": "13"
+ }
+*/
+```
\ No newline at end of file
diff --git a/src/helpers/readme.md b/src/helpers/readme.md
deleted file mode 100644
index 5f1efd2c8..000000000
--- a/src/helpers/readme.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# @ua-parser-js/helpers
-
-This package contains a collection of utility methods for [UAParser.js](https://github.com/faisalman/ua-parser-js)
-
-```sh
-npm i @ua-parser-js/helpers
-```
-
-### * `isFrozenUA(ua:string):boolean`
-
-Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/)
-
-### * `unfreezeUA():Promise`
-
-Construct new unfreezed user-agent string using real data from client hints
-
-### * `UACHParser(headers: object): object`
-
-Parse client hints HTTP headers (sec-ch-ua) into its JS API equivalent
-
-## Code Example
-
-```js
-import { isFrozenUA } from '@ua-parser-js/helpers';
-
-const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
-const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
-
-console.log(isFrozenUA(regularMobileUA));
-// false
-
-console.log(isFrozenUA(freezedMobileUA));
-// true
-```
-
-```js
-import { unfreezeUA } from '@ua-parser-js/helpers';
-
-/*
- Suppose we're in a browser having this client hints data:
-
- {
- fullVersionList: [
- {
- brand: 'New Browser',
- version: '110.1.2.3'
- },
- {
- brand: 'Chromium',
- version: '110.1.2.3'
- },
- {
- brand: 'Not(A:Brand',
- version: '110'
- }
- ],
- platform: 'Windows',
- platformVersion: '13.0.0'
- }
-
- And a freezed user-agent:
-
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
-*/
-
-unfreezeUA()
- .then(ua => console.log(ua));
-// 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
-```
-
-```js
-import { UACHParser } from '@ua-parser-js/helpers';
-
-/*
- Suppose we're in a server having this client hints data:
-
- const headers = {
- 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
- 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : 'arm',
- 'sec-ch-ua-bitness' : '64',
- 'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : 'Pixel 99',
- 'sec-ch-ua-platform' : 'Linux',
- 'sec-ch-ua-platform-version' : '13',
- 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
- };
-*/
-
-const userAgentData = UACHParser(headers);
-
-console.log(userAgentData);
-/*
- {
- "architecture": "arm",
- "bitness": "64",
- "brands": [
- {
- "brand": "Chromium",
- "version": "93"
- },
- {
- "brand": "Google Chrome",
- "version": "93"
- },
- {
- "brand": " Not;A Brand",
- "version": "99"
- }
- ],
- "fullVersionList": [
- {
- "brand": "Chromium",
- "version": "93.0.1.2"
- },
- {
- "brand": "Google Chrome",
- "version": "93.0.1.2"
- },
- {
- "brand": " Not;A Brand",
- "version": "99.0.1.2"
- }
- ],
- "mobile": true,
- "model": "Pixel 99",
- "platform": "Linux",
- "platformVersion": "13"
- }
-*/
-```
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs
deleted file mode 100644
index bf0dd6650..000000000
--- a/src/helpers/ua-parser-helpers.mjs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Generated ESM version of UAParser.js helpers
-// DO NOT EDIT THIS FILE!
-// Source: /src/helpers/ua-parser-helpers.js
-
-///////////////////////////////////////////////
-/* Helpers for UAParser.js
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-//////////////////////////////////////////////
-
-/*jshint esversion: 6 */
-
-/*
- # Reference:
- https://www.chromium.org/updates/ua-reduction/
-
- # Desktop
- ---
- Format:
- Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
-
- Possible values:
- - Windows NT 10.0; Win64; x64
- - Macintosh; Intel Mac OS X 10_15_7
- - X11; Linux x86_64
- - X11; CrOS x86_64 14541.0.0
- - Fuchsia
-
- # Mobile & Tablet: (except iOS/Android WebView)
- ---
- Format:
- Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
-
- Possible values:
- - "Mobile"
- - "" (empty string for Tablets & Desktop)
-*/
-
-const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua);
-
-export {
- isFrozenUA
-};
\ No newline at end of file
diff --git a/src/user-agent-helpers/package.json b/src/user-agent-helpers/package.json
new file mode 100644
index 000000000..aef3208e2
--- /dev/null
+++ b/src/user-agent-helpers/package.json
@@ -0,0 +1,32 @@
+{
+ "title": "User-Agent Helpers",
+ "name": "@ua-parser-js/user-agent-helpers",
+ "version": "0.0.1",
+ "author": "Faisal Salman ",
+ "description": "A collection of utility methods for working with user-agent",
+ "main": "user-agent-helpers.js",
+ "module": "user-agent-helpers.mjs",
+ "scripts": {
+ "test": "mocha ../../test/mocha-test-helpers"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/faisalman/ua-parser-js.git"
+ },
+ "keywords": [
+ "ua-parser-js",
+ "browser-detection",
+ "device-detection",
+ "os-detection",
+ "user-agent",
+ "client-hints"
+ ],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/faisalman/ua-parser-js/issues"
+ },
+ "homepage": "https://github.com/faisalman/ua-parser-js#readme",
+ "dependencies": {
+ "@ua-parser-js/client-hints-helpers": "*"
+ }
+}
diff --git a/src/user-agent-helpers/readme.md b/src/user-agent-helpers/readme.md
new file mode 100644
index 000000000..b2eefd25b
--- /dev/null
+++ b/src/user-agent-helpers/readme.md
@@ -0,0 +1,67 @@
+# @ua-parser-js/user-agent-helpers
+
+This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent.
+
+```sh
+npm i @ua-parser-js/user-agent-helpers
+```
+
+### * `isFrozenUA(ua:string):boolean`
+
+Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/)
+
+### * `unfreezeUA([ua:string,ch:object]|[headers:object]):Promise`
+
+Construct new unfreezed user-agent string using real data from client hints
+
+## Code Example
+
+```js
+import { isFrozenUA } from '@ua-parser-js/user-agent-helpers';
+
+const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
+const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
+
+console.log(isFrozenUA(regularMobileUA));
+// false
+
+console.log(isFrozenUA(freezedMobileUA));
+// true
+```
+
+```js
+import { unfreezeUA } from '@ua-parser-js/user-agent-helpers';
+
+/*
+ Suppose we're in a browser having this client hints data:
+
+ {
+ fullVersionList: [
+ {
+ brand: 'New Browser',
+ version: '110.1.2.3'
+ },
+ {
+ brand: 'Chromium',
+ version: '110.1.2.3'
+ },
+ {
+ brand: 'Not(A:Brand',
+ version: '110'
+ }
+ ],
+ platform: 'Windows',
+ platformVersion: '13.0.0',
+ architecture: 'arm'
+ }
+
+ With a frozen user-agent:
+
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
+*/
+
+// Now let's generate a complete user-agent:
+unfreezeUA()
+ .then(newUA => console.log(newUA));
+// 'Mozilla/5.0 (Windows NT 11.0; ARM) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
+```
\ No newline at end of file
diff --git a/src/user-agent-helpers/user-agent-helpers.d.ts b/src/user-agent-helpers/user-agent-helpers.d.ts
new file mode 100644
index 000000000..ae63f9fe7
--- /dev/null
+++ b/src/user-agent-helpers/user-agent-helpers.d.ts
@@ -0,0 +1,4 @@
+export function isFrozenUA(ua: string): boolean;
+export function unfreezeUA(): Promise;
+export function unfreezeUA(ua: string, ch: ClientHintsJSHighEntropy): Promise;
+export function unfreezeUA(headers: ClientHintsHTTPHeaders): Promise;
\ No newline at end of file
diff --git a/src/helpers/ua-parser-helpers.js b/src/user-agent-helpers/user-agent-helpers.js
similarity index 63%
rename from src/helpers/ua-parser-helpers.js
rename to src/user-agent-helpers/user-agent-helpers.js
index 26d28f726..1b74e1d8e 100644
--- a/src/helpers/ua-parser-helpers.js
+++ b/src/user-agent-helpers/user-agent-helpers.js
@@ -1,12 +1,14 @@
-///////////////////////////////////////////////
-/* A collection of utility methods for UAParser.js
+////////////////////////////////////////////////////
+/* A collection of utility methods for user-agent
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman
MIT License */
-//////////////////////////////////////////////
+///////////////////////////////////////////////////
/*jshint esversion: 11 */
+const { UACHParser } = require('@ua-parser-js/client-hints-helpers');
+
/*
# Reference:
https://www.chromium.org/updates/ua-reduction/
@@ -89,84 +91,7 @@ const unfreezeUA = async (ua, ch) => {
return ua;
};
-const UACHMap = {
- 'sec-ch-ua-arch' : {
- prop : 'architecture',
- type : 'sf-string'
- },
- 'sec-ch-ua-bitness' : {
- prop : 'bitness',
- type : 'sf-string'
- },
- 'sec-ch-ua' : {
- prop : 'brands',
- type : 'sf-list'
- },
- 'sec-ch-ua-form-factor' : {
- prop : 'formFactor',
- type : 'sf-string'
- },
- 'sec-ch-ua-full-version-list' : {
- prop : 'fullVersionList',
- type : 'sf-list'
- },
- 'sec-ch-ua-mobile' : {
- prop : 'mobile',
- type : 'sf-boolean',
- },
- 'sec-ch-ua-model' : {
- prop : 'model',
- type : 'sf-string',
- },
- 'sec-ch-ua-platform' : {
- prop : 'platform',
- type : 'sf-string'
- },
- 'sec-ch-ua-platform-version' : {
- prop : 'platformVersion',
- type : 'sf-string'
- },
- 'sec-ch-ua-wow64' : {
- prop : 'wow64',
- type : 'sf-boolean'
- }
-};
-
-const UACHParser = (headers) => {
- const parse = (str, type) => {
- if (!str) {
- return '';
- }
- switch (type) {
- case 'sf-boolean':
- return /\?1/.test(str);
- case 'sf-list':
- return str.replace(/\\?\"/g, '')
- .split(', ')
- .map(brands => {
- const [brand, version] = brands.split(';v=');
- return {
- brand : brand,
- version : version
- };
- });
- case 'sf-string':
- default:
- return str.replace(/\\?\"/g, '');
- }
- };
- let ch = {};
- Object.keys(UACHMap).forEach(field => {
- if (headers.hasOwnProperty(field)) {
- const { prop, type } = UACHMap[field];
- ch[prop] = parse(headers[field], type);
- }
- });
- return ch;
-};
-
module.exports = {
isFrozenUA,
- unfreezeUA,
- UACHParser
+ unfreezeUA
};
\ No newline at end of file
diff --git a/src/user-agent-helpers/user-agent-helpers.mjs b/src/user-agent-helpers/user-agent-helpers.mjs
new file mode 100644
index 000000000..a125a49ae
--- /dev/null
+++ b/src/user-agent-helpers/user-agent-helpers.mjs
@@ -0,0 +1,101 @@
+// Generated ESM version of @ua-parser-js/user-agent-helpers
+// DO NOT EDIT THIS FILE!
+// Source: /src/user-agent-helpers/user-agent-helpers.js
+
+////////////////////////////////////////////////////
+/* A collection of utility methods for user-agent
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+///////////////////////////////////////////////////
+
+/*jshint esversion: 11 */
+
+import { UACHParser } from '@ua-parser-js/client-hints-helpers';
+
+/*
+ # Reference:
+ https://www.chromium.org/updates/ua-reduction/
+
+ # Desktop
+ ---
+ Format:
+ Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
+
+ Possible values:
+ - Windows NT 10.0; Win64; x64
+ - Macintosh; Intel Mac OS X 10_15_7
+ - X11; Linux x86_64
+ - X11; CrOS x86_64 14541.0.0
+ - Fuchsia
+
+ # Mobile & Tablet: (except iOS/Android WebView)
+ ---
+ Format:
+ Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
+
+ Possible values:
+ - "Mobile"
+ - "" (empty string for Tablets & Desktop)
+*/
+
+const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua);
+
+const unfreezeUA = async (ua, ch) => {
+ const env = typeof navigator == 'undefined' ? 'node' : 'browser';
+ if (env == 'node') {
+ if (!ua['user-agent']) {
+ throw new Error('User-Agent header not found');
+ }
+ ch = UACHParser(ua);
+ ua = ua['user-agent'];
+ } else {
+ ua = ua || navigator.userAgent;
+ ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
+ }
+ if (isFrozenUA(ua) && ch) {
+ switch (ch.platform) {
+ case 'Windows':
+ let [major, minor] = ch.platformVersion
+ .split('.')
+ .map(num => parseInt(num, 10));
+ major = (major < 1) ? '6' : (major >= 13) ? '11' : '10';
+ ua = ua .replace(/(?Windows NT) 10\.0/, `$ ${major}.${minor}`)
+ .replace(/; (?Win64; x64)/,
+ (ch.architecture == 'arm') ?
+ '; ARM' :
+ (ch.wow64) ?
+ '; WOW64' :
+ (ch.architecture == 'x86' && ch.bitness != '64') ?
+ '' : '; $');
+ break;
+ case 'Android':
+ ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`);
+ break;
+ case 'Linux':
+ case 'Chrome OS':
+ ua = ua.replace(/(?x86_64)/,
+ (ch.architecture == 'arm') ?
+ ((ch.bitness == '64') ? 'arm64' : 'arm') :
+ (ch.architecture == 'x86' && ch.bitness != '64') ?
+ 'x86' : '$');
+ break;
+ case 'macOS':
+ ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`);
+ break;
+ }
+ if (ch.fullVersionList) {
+ ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /,
+ ch.fullVersionList
+ .filter(browser => !/not.a.brand/i.test(browser.brand))
+ .map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `)
+ .join(''));
+ }
+ }
+ return ua;
+};
+
+export {
+ isFrozenUA,
+ unfreezeUA
+};
\ No newline at end of file
diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js
index 07ba15c6c..526887dd0 100644
--- a/test/mocha-test-helpers.js
+++ b/test/mocha-test-helpers.js
@@ -1,4 +1,5 @@
-const { isFrozenUA, unfreezeUA, UACHParser } = require('@ua-parser-js/helpers');
+const { isFrozenUA, unfreezeUA } = require('@ua-parser-js/user-agent-helpers');
+const { UACHParser } = require('@ua-parser-js/client-hints-helpers');
const assert = require('assert');
describe('isFrozenUA()', () => {
@@ -54,7 +55,7 @@ describe('unfreezeUA()', () => {
});
describe('UACHParser()', () => {
- it('parse client hints HTTP headers (sec-ch-ua) into a JavaScript object', () => {
+ it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => {
assert.deepEqual(UACHParser(headers), {
"architecture": "arm",
"bitness": "64",
diff --git a/test/playwright-test-helpers.spec.mjs b/test/playwright-test-helpers.spec.mjs
deleted file mode 100644
index c5bc65e70..000000000
--- a/test/playwright-test-helpers.spec.mjs
+++ /dev/null
@@ -1,47 +0,0 @@
-// @ts-check
-import { test, expect } from '@playwright/test';
-import { unfreezeUA } from '@ua-parser-js/helpers';
-
-test('test for unfreezeUA() method', async ({ page }) => {
-
-
- await page.addInitScript(() => {
- Object.defineProperty(navigator, 'userAgentData', {
- value: {
- brands: [],
- platform: 'Windows',
- mobile: false,
- getHighEntropyValues: () => {
- return Promise.resolve({
- architecture: 'x86',
- bitness: '64',
- fullVersionList: [
- {
- brand: 'New Browser',
- version: '110.1.2.3'
- },
- {
- brand: 'Chromium',
- version: '110.1.2.3'
- },
- {
- brand: 'Not(A:Brand',
- version: '110'
- }
- ],
- platform: 'Windows',
- platformVersion: '0.3'
- });
- }
- }
- });
- Object.defineProperty(navigator, 'userAgent', {
- value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
- });
- });
- await page.goto('about:blank');
- await page.addScriptTag({ path: './src/helpers/ua-parser-helpers.js' });
- // @ts-ignore
- const ua = await page.evaluate(async () => await unfreezeUA());
- expect(ua).toBe('Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36');
-});
\ No newline at end of file
From f538018f8e4813f1ca96dc8edeb5ab6958444862 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Thu, 24 Aug 2023 21:08:33 +0700
Subject: [PATCH 119/388] Update package.json/package-lock.json & remove
bower.json (at last!)
---
bower.json | 17 -----------------
package-lock.json | 5 ++++-
package.json | 9 +++++++--
3 files changed, 11 insertions(+), 20 deletions(-)
delete mode 100644 bower.json
diff --git a/bower.json b/bower.json
deleted file mode 100644
index a8c0ae384..000000000
--- a/bower.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "name": "ua-parser-js",
- "version": "2.0.0-alpha.3",
- "authors": [
- "Faisal Salman "
- ],
- "private": false,
- "main": "src/ua-parser.js",
- "ignore": [
- "build",
- "node_modules",
- "bower_components",
- "test",
- "tests"
- ],
- "dependencies": {}
-}
diff --git a/package-lock.json b/package-lock.json
index 613ed7399..92bbfbdb4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3797,7 +3797,10 @@
"src/user-agent-helpers": {
"name": "@ua-parser-js/user-agent-helpers",
"version": "0.0.1",
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "@ua-parser-js/client-hints-helpers": "*"
+ }
}
}
}
diff --git a/package.json b/package.json
index 205980ee7..f687f886c 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,11 @@
"device",
"cpu",
"jquery-plugin",
- "ecosystem:jquery"
+ "ecosystem:jquery",
+ "ua-parser-js",
+ "browser-detection",
+ "device-detection",
+ "os-detection"
],
"homepage": "https://github.com/faisalman/ua-parser-js",
"contributors": [
@@ -215,6 +219,7 @@
}
],
"workspaces": [
- "src/helpers"
+ "src/client-hints-helpers",
+ "src/user-agent-helpers"
]
}
From 15226914260142b0ef96b081565d5f30198ed521 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 26 Aug 2023 04:55:07 +0700
Subject: [PATCH 120/388] [ua-client-hints] Refactor `UAClientHints`
---
package-lock.json | 10 +-
package.json | 2 +-
script/build-module.js | 6 +-
.../client-hints-helpers.d.ts | 35 -----
.../client-hints-helpers.js | 88 ------------
.../client-hints-helpers.mjs | 92 ------------
src/client-hints-helpers/readme.md | 75 ----------
.../package.json | 10 +-
src/ua-client-hints/readme.md | 128 +++++++++++++++++
src/ua-client-hints/ua-client-hints.d.ts | 39 +++++
src/ua-client-hints/ua-client-hints.js | 130 +++++++++++++++++
src/ua-client-hints/ua-client-hints.mjs | 134 ++++++++++++++++++
test/mocha-test-helpers.js | 28 ++--
13 files changed, 466 insertions(+), 311 deletions(-)
delete mode 100644 src/client-hints-helpers/client-hints-helpers.d.ts
delete mode 100644 src/client-hints-helpers/client-hints-helpers.js
delete mode 100644 src/client-hints-helpers/client-hints-helpers.mjs
delete mode 100644 src/client-hints-helpers/readme.md
rename src/{client-hints-helpers => ua-client-hints}/package.json (78%)
create mode 100644 src/ua-client-hints/readme.md
create mode 100644 src/ua-client-hints/ua-client-hints.d.ts
create mode 100644 src/ua-client-hints/ua-client-hints.js
create mode 100644 src/ua-client-hints/ua-client-hints.mjs
diff --git a/package-lock.json b/package-lock.json
index 92bbfbdb4..f2262f30d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,7 +23,7 @@
],
"license": "MIT",
"workspaces": [
- "src/client-hints-helpers",
+ "src/ua-client-hints",
"src/user-agent-helpers"
],
"devDependencies": {
@@ -768,6 +768,10 @@
"resolved": "src/client-hints-helpers",
"link": true
},
+ "node_modules/@ua-parser-js/ua-client-hints": {
+ "resolved": "src/ua-client-hints",
+ "link": true
+ },
"node_modules/@ua-parser-js/user-agent-helpers": {
"resolved": "src/user-agent-helpers",
"link": true
@@ -3794,6 +3798,10 @@
"extraneous": true,
"license": "MIT"
},
+ "src/ua-client-hints": {
+ "version": "0.0.1",
+ "license": "MIT"
+ },
"src/user-agent-helpers": {
"name": "@ua-parser-js/user-agent-helpers",
"version": "0.0.1",
diff --git a/package.json b/package.json
index f687f886c..e4b620706 100644
--- a/package.json
+++ b/package.json
@@ -219,7 +219,7 @@
}
],
"workspaces": [
- "src/client-hints-helpers",
+ "src/ua-client-hints",
"src/user-agent-helpers"
]
}
diff --git a/script/build-module.js b/script/build-module.js
index 3b00317f6..d3c793556 100755
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -54,9 +54,9 @@ const modules = [
replacements : []
},
{
- src : 'src/client-hints-helpers/client-hints-helpers.js',
- dest : 'src/client-hints-helpers/client-hints-helpers.mjs',
- title : '@ua-parser-js/client-hints-helpers',
+ src : 'src/ua-client-hints/ua-client-hints.js',
+ dest : 'src/ua-client-hints/ua-client-hints.mjs',
+ title : '@ua-parser-js/ua-client-hints',
replacements : []
}
];
diff --git a/src/client-hints-helpers/client-hints-helpers.d.ts b/src/client-hints-helpers/client-hints-helpers.d.ts
deleted file mode 100644
index 00f98f84b..000000000
--- a/src/client-hints-helpers/client-hints-helpers.d.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-interface IBrowser {
- brand: string;
- version: string;
-}
-
-interface ClientHintsJSLowEntropy {
- brands: Array;
- mobile: boolean;
- platform: string;
-}
-
-export interface ClientHintsJSHighEntropy extends ClientHintsJSLowEntropy {
- architecture?: string;
- bitness?: string;
- formFactor?: string;
- fullVersionList?: Array;
- model?: string;
- platformVersion?: string;
- wow64?: boolean;
-};
-
-export interface ClientHintsHTTPHeaders {
- 'sec-ch-ua-arch'?: string;
- 'sec-ch-ua-bitness'?: string;
- 'sec-ch-ua'?: string;
- 'sec-ch-ua-form-factor'?: string;
- 'sec-ch-ua-full-version-list'?: string;
- 'sec-ch-ua-mobile'?: string;
- 'sec-ch-ua-model'?: string;
- 'sec-ch-ua-platform'?: string;
- 'sec-ch-ua-platform-version'?: string;
- 'sec-ch-ua-wow64'?: string;
-}
-
-export function UACHParser(headers: ClientHintsHTTPHeaders): ClientHintsJSHighEntropy;
\ No newline at end of file
diff --git a/src/client-hints-helpers/client-hints-helpers.js b/src/client-hints-helpers/client-hints-helpers.js
deleted file mode 100644
index 5fa3bbc77..000000000
--- a/src/client-hints-helpers/client-hints-helpers.js
+++ /dev/null
@@ -1,88 +0,0 @@
-////////////////////////////////////////////////////
-/* A collection of utility methods for client-hints
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-///////////////////////////////////////////////////
-
-/*jshint esversion: 11 */
-
-const UACHMap = {
- 'sec-ch-ua-arch' : {
- prop : 'architecture',
- type : 'sf-string'
- },
- 'sec-ch-ua-bitness' : {
- prop : 'bitness',
- type : 'sf-string'
- },
- 'sec-ch-ua' : {
- prop : 'brands',
- type : 'sf-list'
- },
- 'sec-ch-ua-form-factor' : {
- prop : 'formFactor',
- type : 'sf-string'
- },
- 'sec-ch-ua-full-version-list' : {
- prop : 'fullVersionList',
- type : 'sf-list'
- },
- 'sec-ch-ua-mobile' : {
- prop : 'mobile',
- type : 'sf-boolean',
- },
- 'sec-ch-ua-model' : {
- prop : 'model',
- type : 'sf-string',
- },
- 'sec-ch-ua-platform' : {
- prop : 'platform',
- type : 'sf-string'
- },
- 'sec-ch-ua-platform-version' : {
- prop : 'platformVersion',
- type : 'sf-string'
- },
- 'sec-ch-ua-wow64' : {
- prop : 'wow64',
- type : 'sf-boolean'
- }
-};
-
-const UACHParser = (headers) => {
- const parse = (str, type) => {
- if (!str) {
- return '';
- }
- switch (type) {
- case 'sf-boolean':
- return /\?1/.test(str);
- case 'sf-list':
- return str.replace(/\\?\"/g, '')
- .split(', ')
- .map(brands => {
- const [brand, version] = brands.split(';v=');
- return {
- brand : brand,
- version : version
- };
- });
- case 'sf-string':
- default:
- return str.replace(/\\?\"/g, '');
- }
- };
- let ch = {};
- Object.keys(UACHMap).forEach(field => {
- if (headers.hasOwnProperty(field)) {
- const { prop, type } = UACHMap[field];
- ch[prop] = parse(headers[field], type);
- }
- });
- return ch;
-};
-
-module.exports = {
- UACHParser
-};
\ No newline at end of file
diff --git a/src/client-hints-helpers/client-hints-helpers.mjs b/src/client-hints-helpers/client-hints-helpers.mjs
deleted file mode 100644
index 184e0002d..000000000
--- a/src/client-hints-helpers/client-hints-helpers.mjs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Generated ESM version of @ua-parser-js/client-hints-helpers
-// DO NOT EDIT THIS FILE!
-// Source: /src/client-hints-helpers/client-hints-helpers.js
-
-////////////////////////////////////////////////////
-/* A collection of utility methods for client-hints
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-///////////////////////////////////////////////////
-
-/*jshint esversion: 11 */
-
-const UACHMap = {
- 'sec-ch-ua-arch' : {
- prop : 'architecture',
- type : 'sf-string'
- },
- 'sec-ch-ua-bitness' : {
- prop : 'bitness',
- type : 'sf-string'
- },
- 'sec-ch-ua' : {
- prop : 'brands',
- type : 'sf-list'
- },
- 'sec-ch-ua-form-factor' : {
- prop : 'formFactor',
- type : 'sf-string'
- },
- 'sec-ch-ua-full-version-list' : {
- prop : 'fullVersionList',
- type : 'sf-list'
- },
- 'sec-ch-ua-mobile' : {
- prop : 'mobile',
- type : 'sf-boolean',
- },
- 'sec-ch-ua-model' : {
- prop : 'model',
- type : 'sf-string',
- },
- 'sec-ch-ua-platform' : {
- prop : 'platform',
- type : 'sf-string'
- },
- 'sec-ch-ua-platform-version' : {
- prop : 'platformVersion',
- type : 'sf-string'
- },
- 'sec-ch-ua-wow64' : {
- prop : 'wow64',
- type : 'sf-boolean'
- }
-};
-
-const UACHParser = (headers) => {
- const parse = (str, type) => {
- if (!str) {
- return '';
- }
- switch (type) {
- case 'sf-boolean':
- return /\?1/.test(str);
- case 'sf-list':
- return str.replace(/\\?\"/g, '')
- .split(', ')
- .map(brands => {
- const [brand, version] = brands.split(';v=');
- return {
- brand : brand,
- version : version
- };
- });
- case 'sf-string':
- default:
- return str.replace(/\\?\"/g, '');
- }
- };
- let ch = {};
- Object.keys(UACHMap).forEach(field => {
- if (headers.hasOwnProperty(field)) {
- const { prop, type } = UACHMap[field];
- ch[prop] = parse(headers[field], type);
- }
- });
- return ch;
-};
-
-export {
- UACHParser
-};
\ No newline at end of file
diff --git a/src/client-hints-helpers/readme.md b/src/client-hints-helpers/readme.md
deleted file mode 100644
index b1f569e38..000000000
--- a/src/client-hints-helpers/readme.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# @ua-parser-js/client-hints-helpers
-
-This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client-hints.
-
-```sh
-npm i @ua-parser-js/client-hints-helpers
-```
-
-### * `UACHParser(headers:object):object`
-
-Parse user-agent client-hints HTTP headers (sec-ch-ua) into its JS API equivalent
-
-## Code Example
-
-```js
-import { UACHParser } from '@ua-parser-js/client-hints-helpers';
-
-/*
- Suppose we're in a server having this client hints data:
-
- const headers = {
- 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
- 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : 'arm',
- 'sec-ch-ua-bitness' : '64',
- 'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : 'Pixel 99',
- 'sec-ch-ua-platform' : 'Linux',
- 'sec-ch-ua-platform-version' : '13',
- 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
- };
-*/
-
-const userAgentData = UACHParser(headers);
-
-console.log(userAgentData);
-/*
- {
- "architecture": "arm",
- "bitness": "64",
- "brands": [
- {
- "brand": "Chromium",
- "version": "93"
- },
- {
- "brand": "Google Chrome",
- "version": "93"
- },
- {
- "brand": " Not;A Brand",
- "version": "99"
- }
- ],
- "fullVersionList": [
- {
- "brand": "Chromium",
- "version": "93.0.1.2"
- },
- {
- "brand": "Google Chrome",
- "version": "93.0.1.2"
- },
- {
- "brand": " Not;A Brand",
- "version": "99.0.1.2"
- }
- ],
- "mobile": true,
- "model": "Pixel 99",
- "platform": "Linux",
- "platformVersion": "13"
- }
-*/
-```
\ No newline at end of file
diff --git a/src/client-hints-helpers/package.json b/src/ua-client-hints/package.json
similarity index 78%
rename from src/client-hints-helpers/package.json
rename to src/ua-client-hints/package.json
index 39a2dc2e6..0ea28cdb9 100644
--- a/src/client-hints-helpers/package.json
+++ b/src/ua-client-hints/package.json
@@ -1,11 +1,11 @@
{
- "title": "Client-Hints Helpers",
- "name": "@ua-parser-js/client-hints-helpers",
+ "title": "User-Agent Client Hints",
+ "name": "@ua-parser-js/ua-client-hints",
"version": "0.0.1",
"author": "Faisal Salman ",
- "description": "A collection of utility methods for working with client-hints",
- "main": "client-hints-helpers.js",
- "module": "client-hints-helpers.mjs",
+ "description": "A collection of utility methods for working with user-agent client hints",
+ "main": "ua-client-hints.js",
+ "module": "ua-client-hints.mjs",
"scripts": {
"test": "mocha ../../test/mocha-test-helpers"
},
diff --git a/src/ua-client-hints/readme.md b/src/ua-client-hints/readme.md
new file mode 100644
index 000000000..253f4ebe7
--- /dev/null
+++ b/src/ua-client-hints/readme.md
@@ -0,0 +1,128 @@
+# @ua-parser-js/ua-client-hints
+
+This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client hints.
+
+```sh
+npm i @ua-parser-js/ua-client-hints
+```
+
+### * `getUAData([props:array]):object`
+
+Get user-agent client hints values of current instance in form of JS object representation
+
+### * `setUAData([uaData:object]):UAClientHints`
+
+Set values of user-agent client hints for the current instance either from navigator.userAgentData or from HTTP headers (Sec-CH-UA-*)
+
+### * `getSerializedUAData([props:array]):object`
+
+Get user-agent client hints values of current instance in form of HTTP headers string representation (Sec-CH-UA-*)
+
+## Code Example
+
+```js
+import { UAClientHints } from '@ua-parser-js/ua-client-hints';
+
+/*
+ Suppose we're in a server having this client hints data:
+
+ const httpHeaders = {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
+ 'sec-ch-ua-arch' : 'arm',
+ 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Pixel 99',
+ 'sec-ch-ua-platform' : 'Linux',
+ 'sec-ch-ua-platform-version' : '13'
+ };
+*/
+
+const uaCH = new UAClientHints();
+uaCH.setUAData(httpHeaders);
+const uaCHData1 = uaCH.getUAData();
+const uaCHData2 = uaCH.getUAData(['architecture', 'bitness']);
+
+console.log(uaCHData1);
+/*
+ {
+ "architecture": "arm",
+ "bitness": "64",
+ "brands": [
+ {
+ "brand": "Chromium",
+ "version": "93"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99"
+ }
+ ],
+ "fullVersionList": [
+ {
+ "brand": "Chromium",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": " Not;A Brand",
+ "version": "99.0.1.2"
+ }
+ ],
+ "mobile": true,
+ "model": "Pixel 99",
+ "platform": "Linux",
+ "platformVersion": "13",
+ "wow64": null,
+ "formFactor": null
+ }
+*/
+
+console.log(uaCHData2);
+/*
+ {
+ "architecture": "arm",
+ "bitness": "64"
+ }
+*/
+
+uaCH.setUAData({
+ "wow64" : true,
+ "formFactor" : "Automotive"
+});
+
+const headersData1 = uaCH.getSerializedUAData();
+const headersData2 = uaCH.getSerializedUAData(['brand', 'mobile', 'model']);
+
+console.log(headersData1);
+/*
+ {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
+ 'sec-ch-ua-arch' : 'arm',
+ 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Pixel 99',
+ 'sec-ch-ua-platform' : 'Linux',
+ 'sec-ch-ua-platform-version' : '13',
+ 'sec-ch-ua-wow64' : '?1',
+ 'sec-ch-ua-form-factor' : 'Automotive'
+ };
+*/
+
+console.log(headersData2);
+/*
+ {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : 'Pixel 99'
+ };
+*/
+```
\ No newline at end of file
diff --git a/src/ua-client-hints/ua-client-hints.d.ts b/src/ua-client-hints/ua-client-hints.d.ts
new file mode 100644
index 000000000..307765848
--- /dev/null
+++ b/src/ua-client-hints/ua-client-hints.d.ts
@@ -0,0 +1,39 @@
+export type UABrowser = {
+ brand: string | null,
+ version: string | null
+};
+
+export type UADataType = boolean | string | Array | null;
+export type UADataField =
+ 'brands' |
+ 'mobile' |
+ 'platform' |
+ 'architecture' |
+ 'bitness' |
+ 'formFactor' |
+ 'fullVersionList' |
+ 'model' |
+ 'platformVersion' |
+ 'wow64';
+
+export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list';
+export type HeaderField =
+ 'sec-ch-ua-arch' |
+ 'sec-ch-ua-bitness' |
+ 'sec-ch-ua' |
+ 'sec-ch-ua-form-factor' |
+ 'sec-ch-ua-full-version-list' |
+ 'sec-ch-ua-mobile' |
+ 'sec-ch-ua-model' |
+ 'sec-ch-ua-platform' |
+ 'sec-ch-ua-platform-version' |
+ 'sec-ch-ua-wow64';
+
+export class UAClientHints {
+ #ch: Map;
+ #parseHeader(str: string, type: HeaderType): UADataType;
+ #serialize(data: UADataType, type: HeaderType): string;
+ getSerializedUAData(): Record;
+ getUAData(props?: Array): Record;
+ setUAData(uaData: Record | Record): UAClientHints;
+};
\ No newline at end of file
diff --git a/src/ua-client-hints/ua-client-hints.js b/src/ua-client-hints/ua-client-hints.js
new file mode 100644
index 000000000..d4ddc6077
--- /dev/null
+++ b/src/ua-client-hints/ua-client-hints.js
@@ -0,0 +1,130 @@
+//////////////////////////////////////////////
+/* A collection of utility methods for
+ working with user-agent client hints
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+/////////////////////////////////////////////
+
+/*jshint esversion: 11 */
+
+const fieldType = Object.freeze({
+ Boolean : 'sf-boolean',
+ List : 'sf-list',
+ String : 'sf-string'
+});
+
+const uaCHMap = Object.freeze({
+ architecture : {
+ field : 'Sec-CH-UA-Arch',
+ type : fieldType.String
+ },
+ bitness : {
+ field : 'Sec-CH-UA-Bitness',
+ type : fieldType.String
+ },
+ brands : {
+ field : 'Sec-CH-UA',
+ type : fieldType.List
+ },
+ formFactor : {
+ field : 'Sec-CH-UA-Form-Factor',
+ type : fieldType.String
+ },
+ fullVersionList : {
+ field : 'Sec-CH-UA-Full-Version-List',
+ type : fieldType.List
+ },
+ mobile : {
+ field : 'Sec-CH-UA-Mobile',
+ type : fieldType.Boolean
+ },
+ model : {
+ field : 'Sec-CH-UA-Model',
+ type : fieldType.String
+ },
+ platform : {
+ field : 'Sec-CH-UA-Platform',
+ type : fieldType.String
+ },
+ platformVersion : {
+ field : 'Sec-CH-UA-Platform-Version',
+ type : fieldType.String
+ },
+ wow64 : {
+ field : 'Sec-CH-UA-WOW64',
+ type : fieldType.Boolean
+ }
+});
+
+class UAClientHints {
+
+ #uach = new Map();
+
+ constructor () {
+ for (const key in uaCHMap) {
+ this.#uach.set(key, null);
+ }
+ return this;
+ };
+
+ #parseHeader (str, type) {
+ if (!str) {
+ return null;
+ }
+ switch (type) {
+ case fieldType.Boolean:
+ return /\?1/.test(str);
+ case fieldType.List:
+ return str.replace(/\\?\"/g, '')
+ .split(',')
+ .map(brands => {
+ const [brand, version] = brands.trim().split(';v=');
+ return {
+ brand : brand,
+ version : version
+ };
+ });
+ case fieldType.String:
+ return str.replace(/\s*\\?\"\s*/g, '');
+ default:
+ return '';
+ }
+ };
+
+ #serialize(data, type) {
+ throw new Error('Not implemented yet');
+ //return '';
+ }
+
+ getSerializedUAData() {
+ throw new Error('Not implemented yet');
+ //let http = {};
+ //return http;
+ }
+
+ getUAData(props) {
+ if (props) {
+ return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)]));
+ }
+ return Object.fromEntries(this.#uach);
+ }
+
+ setUAData(uaDataValues) {
+ if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) {
+ for (const val in uaCHMap) {
+ const { field, type } = uaCHMap[val];
+ this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type));
+ }
+ } else {
+ for (const value in uaDataValues) {
+ if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]);
+ }
+ }
+ return this;
+ };
+}
+
+module.exports = {
+ UAClientHints
+};
\ No newline at end of file
diff --git a/src/ua-client-hints/ua-client-hints.mjs b/src/ua-client-hints/ua-client-hints.mjs
new file mode 100644
index 000000000..047f5c828
--- /dev/null
+++ b/src/ua-client-hints/ua-client-hints.mjs
@@ -0,0 +1,134 @@
+// Generated ESM version of @ua-parser-js/ua-client-hints
+// DO NOT EDIT THIS FILE!
+// Source: /src/ua-client-hints/ua-client-hints.js
+
+//////////////////////////////////////////////
+/* A collection of utility methods for
+ working with user-agent client hints
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+/////////////////////////////////////////////
+
+/*jshint esversion: 11 */
+
+const fieldType = Object.freeze({
+ Boolean : 'sf-boolean',
+ List : 'sf-list',
+ String : 'sf-string'
+});
+
+const uaCHMap = Object.freeze({
+ architecture : {
+ field : 'Sec-CH-UA-Arch',
+ type : fieldType.String
+ },
+ bitness : {
+ field : 'Sec-CH-UA-Bitness',
+ type : fieldType.String
+ },
+ brands : {
+ field : 'Sec-CH-UA',
+ type : fieldType.List
+ },
+ formFactor : {
+ field : 'Sec-CH-UA-Form-Factor',
+ type : fieldType.String
+ },
+ fullVersionList : {
+ field : 'Sec-CH-UA-Full-Version-List',
+ type : fieldType.List
+ },
+ mobile : {
+ field : 'Sec-CH-UA-Mobile',
+ type : fieldType.Boolean
+ },
+ model : {
+ field : 'Sec-CH-UA-Model',
+ type : fieldType.String
+ },
+ platform : {
+ field : 'Sec-CH-UA-Platform',
+ type : fieldType.String
+ },
+ platformVersion : {
+ field : 'Sec-CH-UA-Platform-Version',
+ type : fieldType.String
+ },
+ wow64 : {
+ field : 'Sec-CH-UA-WOW64',
+ type : fieldType.Boolean
+ }
+});
+
+class UAClientHints {
+
+ #uach = new Map();
+
+ constructor () {
+ for (const key in uaCHMap) {
+ this.#uach.set(key, null);
+ }
+ return this;
+ };
+
+ #parseHeader (str, type) {
+ if (!str) {
+ return null;
+ }
+ switch (type) {
+ case fieldType.Boolean:
+ return /\?1/.test(str);
+ case fieldType.List:
+ return str.replace(/\\?\"/g, '')
+ .split(',')
+ .map(brands => {
+ const [brand, version] = brands.trim().split(';v=');
+ return {
+ brand : brand,
+ version : version
+ };
+ });
+ case fieldType.String:
+ return str.replace(/\s*\\?\"\s*/g, '');
+ default:
+ return '';
+ }
+ };
+
+ #serialize(data, type) {
+ throw new Error('Not implemented yet');
+ //return '';
+ }
+
+ getSerializedUAData() {
+ throw new Error('Not implemented yet');
+ //let http = {};
+ //return http;
+ }
+
+ getUAData(props) {
+ if (props) {
+ return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)]));
+ }
+ return Object.fromEntries(this.#uach);
+ }
+
+ setUAData(uaDataValues) {
+ if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) {
+ for (const val in uaCHMap) {
+ const { field, type } = uaCHMap[val];
+ this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type));
+ }
+ } else {
+ for (const value in uaDataValues) {
+ if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]);
+ }
+ }
+ return this;
+ };
+}
+
+export {
+ UAClientHints
+};
\ No newline at end of file
diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js
index 526887dd0..2c0b39f05 100644
--- a/test/mocha-test-helpers.js
+++ b/test/mocha-test-helpers.js
@@ -1,5 +1,5 @@
const { isFrozenUA, unfreezeUA } = require('@ua-parser-js/user-agent-helpers');
-const { UACHParser } = require('@ua-parser-js/client-hints-helpers');
+const { UAClientHints } = require('@ua-parser-js/ua-client-hints');
const assert = require('assert');
describe('isFrozenUA()', () => {
@@ -38,12 +38,12 @@ describe('isFrozenUA()', () => {
const headers = {
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : 'arm',
- 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-arch' : '"arm"',
+ 'sec-ch-ua-bitness' : '"64"',
'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : 'Pixel 99',
- 'sec-ch-ua-platform' : 'Linux',
- 'sec-ch-ua-platform-version' : '13',
+ 'sec-ch-ua-model' : '"Pixel 99"',
+ 'sec-ch-ua-platform' : '"Linux"',
+ 'sec-ch-ua-platform-version' : '"13"',
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
};
@@ -54,9 +54,13 @@ describe('unfreezeUA()', () => {
});
});
-describe('UACHParser()', () => {
+describe('Parse CH Headers', () => {
it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => {
- assert.deepEqual(UACHParser(headers), {
+ assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(['architecture', 'bitness']), {
+ "architecture": "arm",
+ "bitness": "64"
+ });
+ assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(), {
"architecture": "arm",
"bitness": "64",
"brands": [
@@ -69,7 +73,7 @@ describe('UACHParser()', () => {
"version": "93"
},
{
- "brand": " Not;A Brand",
+ "brand": "Not;A Brand",
"version": "99"
}
],
@@ -83,14 +87,16 @@ describe('UACHParser()', () => {
"version": "93.0.1.2"
},
{
- "brand": " Not;A Brand",
+ "brand": "Not;A Brand",
"version": "99.0.1.2"
}
],
+ "formFactor": null,
"mobile": true,
"model": "Pixel 99",
"platform": "Linux",
- "platformVersion": "13"
+ "platformVersion": "13",
+ "wow64": null
});
});
});
\ No newline at end of file
From 807dcdbded9180c1813f18df0fd8fcaf20897298 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Wed, 30 Aug 2023 12:55:05 +0700
Subject: [PATCH 121/388] Add eslint to devDependencies to support latest ES
version
---
.eslintrc.json | 13 +
dist/ua-parser.min.js | 2 +-
dist/ua-parser.pack.js | 2 +-
package-lock.json | 857 +++++++++++++++++-
package.json | 4 +-
script/test-all.sh | 1 +
src/enums/ua-parser-enums.mjs | 2 +-
src/extensions/ua-parser-extensions.mjs | 2 +-
src/main/ua-parser.js | 2 +-
src/main/ua-parser.mjs | 19 +-
src/user-agent-helpers/user-agent-helpers.js | 4 +-
src/user-agent-helpers/user-agent-helpers.mjs | 4 +-
test/mocha-test.js | 10 +-
...spec.mjs => playwright-test-main.spec.mjs} | 0
14 files changed, 873 insertions(+), 49 deletions(-)
create mode 100644 .eslintrc.json
rename test/{playwright-test-browser.spec.mjs => playwright-test-main.spec.mjs} (100%)
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 000000000..96aa29a4d
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,13 @@
+{
+ "env": {
+ "browser": true,
+ "commonjs": true,
+ "es2021": true,
+ "node": true
+ },
+ "parserOptions": {
+ "ecmaVersion": "latest"
+ },
+ "rules": {
+ }
+}
diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js
index 46cf21f2f..60de30081 100644
--- a/dist/ua-parser.min.js
+++ b/dist/ua-parser.min.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.3
Copyright © 2012-2023 Faisal Salman
MIT License */
-(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.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",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",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_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,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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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 ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}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(typeof ua===STR_TYPE)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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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-alpha.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",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,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",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",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&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 typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;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 ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}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[FORMFACTOR]){this.set(TYPE,strMapper(uaCH[FORMFACTOR],formFactorMap))}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}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(typeof ua===STR_TYPE)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(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(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 0ba28a22b..93cd67c45 100644
--- a/dist/ua-parser.pack.js
+++ b/dist/ua-parser.pack.js
@@ -1,4 +1,4 @@
/* UAParser.js v2.0.0-alpha.3
Copyright © 2012-2023 Faisal Salman
MIT License */
-!function(i,c){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i),this}]]).setUA(o),this}zi.VERSION="2.0.0-alpha.3",zi.BROWSER=e([h,m,u]),zi.CPU=e([g]),zi.DEVICE=e([p,o,f,r,v,x,a,k,y]),zi.ENGINE=zi.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===l&&define.amd?define(function(){return zi}):typeof i!==b&&(i.UAParser=zi);var Ni,Oi=typeof i!==b&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ni=new zi,Oi.ua=Ni.getResult(),Oi.ua.get=function(){return Ni.getUA()},Oi.ua.set=function(i){Ni.setUA(i);var e,t=Ni.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this);
\ No newline at end of file
+!function(i,u){"use strict";function e(i){for(var e={},t=0;t_?yi(i,_):i),this}]]).setUA(o),this}Hi.VERSION="2.0.0-alpha.3",Hi.BROWSER=e([h,m,l]),Hi.CPU=e([g]),Hi.DEVICE=e([p,t,f,o,v,a,r,x,k]),Hi.ENGINE=Hi.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Hi),exports.UAParser=Hi):typeof define===c&&define.amd?define(function(){return Hi}):typeof i!==b&&(i.UAParser=Hi);var Pi,Ui=typeof i!==b&&(i.jQuery||i.Zepto);Ui&&!Ui.ua&&(Pi=new Hi,Ui.ua=Pi.getResult(),Ui.ua.get=function(){return Pi.getUA()},Ui.ua.set=function(i){Pi.setUA(i);var e,t=Pi.getResult();for(e in t)Ui.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 f2262f30d..4f5336d39 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,9 @@
"src/ua-client-hints",
"src/user-agent-helpers"
],
+ "dependencies": {
+ "eslint": "^8.48.0"
+ },
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
@@ -41,6 +44,14 @@
"node": "*"
}
},
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/@ampproject/remapping": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
@@ -454,6 +465,140 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz",
+ "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.21.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+ "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.48.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz",
+ "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
+ "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
+ },
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
@@ -739,6 +884,38 @@
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/@playwright/test": {
"version": "1.32.3",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz",
@@ -782,6 +959,40 @@
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
"dev": true
},
+ "node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
"node_modules/ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@@ -908,8 +1119,7 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
@@ -987,7 +1197,6 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -1069,6 +1278,14 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -1450,8 +1667,7 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/console-browserify": {
"version": "1.1.0",
@@ -1480,6 +1696,19 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/date-now": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
@@ -1490,7 +1719,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -1536,6 +1764,11 @@
"node": ">=4.0.0"
}
},
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+ },
"node_modules/default-require-extensions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz",
@@ -1584,6 +1817,17 @@
"node": ">=0.3.1"
}
},
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -1685,6 +1929,247 @@
"node": ">=0.8.0"
}
},
+ "node_modules/eslint": {
+ "version": "8.48.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz",
+ "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.48.0",
+ "@humanwhocodes/config-array": "^0.11.10",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.21.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+ "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -1698,6 +2183,44 @@
"node": ">=4"
}
},
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@@ -1716,6 +2239,40 @@
"node": ">=6"
}
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -1738,7 +2295,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -1759,6 +2315,23 @@
"flat": "cli.js"
}
},
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
+ },
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
@@ -1846,8 +2419,7 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/gauge": {
"version": "4.0.4",
@@ -1955,7 +2527,6 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -1987,7 +2558,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -2010,6 +2580,11 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
+ },
"node_modules/growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
@@ -2082,11 +2657,41 @@
}
]
},
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -2095,8 +2700,7 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "1.3.8",
@@ -2120,7 +2724,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2138,7 +2741,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -2155,6 +2757,14 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-plain-obj": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
@@ -2173,8 +2783,7 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.0",
@@ -2310,6 +2919,16 @@
"jshint": "bin/jshint"
}
},
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -2334,11 +2953,22 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -2361,6 +2991,11 @@
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true
},
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+ },
"node_modules/log-symbols": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
@@ -2536,7 +3171,6 @@
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
"integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
- "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -2765,8 +3399,7 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/napi-build-utils": {
"version": "1.0.2",
@@ -2774,6 +3407,11 @@
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
"dev": true
},
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
+ },
"node_modules/node-abi": {
"version": "3.40.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz",
@@ -2865,16 +3503,30 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
"dependencies": {
"wrappy": "1"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -2889,7 +3541,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -2909,11 +3560,21 @@
"node": ">=6"
}
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -2922,11 +3583,18 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -2983,6 +3651,14 @@
"node": ">=10"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/proper-lockfile": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
@@ -3010,6 +3686,33 @@
"once": "^1.3.1"
}
},
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -3104,6 +3807,14 @@
"node": ">=0.4.0"
}
},
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
@@ -3113,11 +3824,19 @@
"node": ">= 4"
}
},
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -3128,6 +3847,28 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -3181,6 +3922,25 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true
},
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -3401,6 +4161,11 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
+ },
"node_modules/tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -3446,6 +4211,28 @@
"node": "*"
}
},
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/uglify-js": {
"version": "3.12.8",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz",
@@ -3497,6 +4284,14 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/url-join": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
@@ -3513,7 +4308,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -3597,8 +4391,7 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/y18n": {
"version": "4.0.3",
@@ -3779,7 +4572,6 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -3799,6 +4591,7 @@
"license": "MIT"
},
"src/ua-client-hints": {
+ "name": "@ua-parser-js/ua-client-hints",
"version": "0.0.1",
"license": "MIT"
},
diff --git a/package.json b/package.json
index e4b620706..3203ace8e 100644
--- a/package.json
+++ b/package.json
@@ -171,7 +171,8 @@
"build": "./script/build-dist.sh && ./script/build-module.js",
"fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync",
"test": "./script/test-all.sh",
- "test:jshint": "jshint src && jshint script",
+ "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:playwright": "playwright test"
@@ -181,6 +182,7 @@
"@babel/traverse": "7.15.4",
"@jazzer.js/core": "^1.4.0",
"@playwright/test": "~1.32.2",
+ "eslint": "^8.48.0",
"jshint": "~2.13.6",
"mocha": "~8.2.0",
"requirejs": "2.3.2",
diff --git a/script/test-all.sh b/script/test-all.sh
index b47652bda..c8b2ca29b 100755
--- a/script/test-all.sh
+++ b/script/test-all.sh
@@ -9,6 +9,7 @@ echo '
- lint js code
'
npm run test:jshint || exit 1
+npm run test:eslint || exit 1
echo '
- test using mocha
diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs
index 6479043bc..702119a55 100644
--- a/src/enums/ua-parser-enums.mjs
+++ b/src/enums/ua-parser-enums.mjs
@@ -1,4 +1,4 @@
-// Generated ESM version of UAParser.js enums
+// Generated ESM version of ua-parser-js/enums
// DO NOT EDIT THIS FILE!
// Source: /src/enums/ua-parser-enums.js
diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs
index 97e07f183..60e7ce16c 100644
--- a/src/extensions/ua-parser-extensions.mjs
+++ b/src/extensions/ua-parser-extensions.mjs
@@ -1,4 +1,4 @@
-// Generated ESM version of UAParser.js extensions
+// Generated ESM version of ua-parser-js/extensions
// DO NOT EDIT THIS FILE!
// Source: /src/extensions/ua-parser-extensions.js
diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js
index 7047e8327..89fa07f1c 100755
--- a/src/main/ua-parser.js
+++ b/src/main/ua-parser.js
@@ -57,7 +57,7 @@
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, BITNESS],
+ CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS],
UA_BROWSER = 'browser',
UA_CPU = 'cpu',
UA_DEVICE = 'device',
diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs
index 6f716d620..85c379a06 100644
--- a/src/main/ua-parser.mjs
+++ b/src/main/ua-parser.mjs
@@ -1,4 +1,4 @@
-// Generated ESM version of UAParser.js
+// Generated ESM version of ua-parser-js
// DO NOT EDIT THIS FILE!
// Source: /src/main/ua-parser.js
@@ -45,6 +45,7 @@
USER_AGENT = 'user-agent',
UA_MAX_LENGTH = 350,
BRANDS = 'brands',
+ FORMFACTOR = 'formFactor',
FULLVERLIST = 'fullVersionList',
PLATFORM = 'platform',
PLATFORMVER = 'platformVersion',
@@ -53,11 +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_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, BITNESS],
+ CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS],
UA_BROWSER = 'browser',
UA_CPU = 'cpu',
UA_DEVICE = 'device',
@@ -258,6 +260,15 @@
'8.1' : 'NT 6.3',
'10' : ['NT 6.4', 'NT 10.0'],
'RT' : 'ARM'
+ },
+
+ formFactorMap = {
+ 'embedded' : 'Automotive',
+ 'mobile' : 'Mobile',
+ 'tablet' : 'Tablet',
+ 'smarttv' : 'TV',
+ 'wearable' : ['VR', 'XR'],
+ '?' : 'Unknown'
};
//////////////
@@ -932,6 +943,7 @@
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
+ [FORMFACTOR, stripQuotes(uach[CH_HEADER_FORM_FACTOR])],
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
]);
} else {
@@ -1046,6 +1058,9 @@
if (uaCH[MODEL]) {
this.set(MODEL, uaCH[MODEL]);
}
+ if (uaCH[FORMFACTOR]) {
+ this.set(TYPE, strMapper(uaCH[FORMFACTOR], formFactorMap));
+ }
break;
case UA_OS:
var osName = uaCH[PLATFORM];
diff --git a/src/user-agent-helpers/user-agent-helpers.js b/src/user-agent-helpers/user-agent-helpers.js
index 1b74e1d8e..cfc9ecf07 100644
--- a/src/user-agent-helpers/user-agent-helpers.js
+++ b/src/user-agent-helpers/user-agent-helpers.js
@@ -7,7 +7,7 @@
/*jshint esversion: 11 */
-const { UACHParser } = require('@ua-parser-js/client-hints-helpers');
+const { UAClientHints } = require('@ua-parser-js/ua-client-hints');
/*
# Reference:
@@ -43,7 +43,7 @@ const unfreezeUA = async (ua, ch) => {
if (!ua['user-agent']) {
throw new Error('User-Agent header not found');
}
- ch = UACHParser(ua);
+ ch = new UAClientHints().setUAData(ua).getUAData();
ua = ua['user-agent'];
} else {
ua = ua || navigator.userAgent;
diff --git a/src/user-agent-helpers/user-agent-helpers.mjs b/src/user-agent-helpers/user-agent-helpers.mjs
index a125a49ae..b8f398e09 100644
--- a/src/user-agent-helpers/user-agent-helpers.mjs
+++ b/src/user-agent-helpers/user-agent-helpers.mjs
@@ -11,7 +11,7 @@
/*jshint esversion: 11 */
-import { UACHParser } from '@ua-parser-js/client-hints-helpers';
+import { UAClientHints } from '@ua-parser-js/ua-client-hints';
/*
# Reference:
@@ -47,7 +47,7 @@ const unfreezeUA = async (ua, ch) => {
if (!ua['user-agent']) {
throw new Error('User-Agent header not found');
}
- ch = UACHParser(ua);
+ ch = new UAClientHints().setUAData(ua).getUAData();
ua = ua['user-agent'];
} else {
ua = ua || navigator.userAgent;
diff --git a/test/mocha-test.js b/test/mocha-test.js
index e1db526ab..7c4cbde11 100644
--- a/test/mocha-test.js
+++ b/test/mocha-test.js
@@ -345,12 +345,12 @@ describe('Map UA-CH headers', function () {
const headers = {
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : 'ARM',
- 'sec-ch-ua-bitness' : '64',
+ 'sec-ch-ua-arch' : '"arm"',
+ 'sec-ch-ua-bitness' : '"64"',
'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : 'Pixel 99',
- 'sec-ch-ua-platform' : 'Windows',
- 'sec-ch-ua-platform-version' : '13',
+ 'sec-ch-ua-model' : '"Pixel 99"',
+ 'sec-ch-ua-platform' : '"Windows"',
+ 'sec-ch-ua-platform-version' : '"13"',
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
};
diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-main.spec.mjs
similarity index 100%
rename from test/playwright-test-browser.spec.mjs
rename to test/playwright-test-main.spec.mjs
From 05a98aceda2e549db5c82eb04d1c827d8f4ed01f Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sun, 3 Sep 2023 01:03:36 +0700
Subject: [PATCH 122/388] Add new package: `gpu-detect` to obtain GPU info from
user-agent
---
package.json | 1 +
script/build-module.js | 6 ++
src/gpu-detect/gpu-detect.d.ts | 3 +
src/gpu-detect/gpu-detect.js | 116 +++++++++++++++++++++++++++++++++
src/gpu-detect/package.json | 25 +++++++
src/gpu-detect/readme.md | 16 +++++
src/gpu-detect/test/index.js | 4 ++
7 files changed, 171 insertions(+)
create mode 100644 src/gpu-detect/gpu-detect.d.ts
create mode 100644 src/gpu-detect/gpu-detect.js
create mode 100644 src/gpu-detect/package.json
create mode 100644 src/gpu-detect/readme.md
create mode 100644 src/gpu-detect/test/index.js
diff --git a/package.json b/package.json
index 3203ace8e..1bff1d7f1 100644
--- a/package.json
+++ b/package.json
@@ -221,6 +221,7 @@
}
],
"workspaces": [
+ "src/gpu-detect",
"src/ua-client-hints",
"src/user-agent-helpers"
]
diff --git a/script/build-module.js b/script/build-module.js
index d3c793556..2c0c4587d 100755
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -47,6 +47,12 @@ const modules = [
title : 'ua-parser-js/extensions',
replacements : []
},
+ {
+ src : 'src/gpu-detect/gpu-detect.js',
+ dest : 'src/gpu-detect/gpu-detect.mjs',
+ title : '@ua-parser-js/gpu-detect',
+ replacements : []
+ },
{
src : 'src/user-agent-helpers/user-agent-helpers.js',
dest : 'src/user-agent-helpers/user-agent-helpers.mjs',
diff --git a/src/gpu-detect/gpu-detect.d.ts b/src/gpu-detect/gpu-detect.d.ts
new file mode 100644
index 000000000..bccc81b6c
--- /dev/null
+++ b/src/gpu-detect/gpu-detect.d.ts
@@ -0,0 +1,3 @@
+export class GPUDetect {
+ static getGPU: { vendor: string, model: string }
+}
\ No newline at end of file
diff --git a/src/gpu-detect/gpu-detect.js b/src/gpu-detect/gpu-detect.js
new file mode 100644
index 000000000..326dbcf43
--- /dev/null
+++ b/src/gpu-detect/gpu-detect.js
@@ -0,0 +1,116 @@
+//////////////////////////////////////////////
+/* Extracts GPU information from user-agent
+ https://github.com/faisalman/ua-parser-js
+ Author: Faisal Salman
+ MIT License */
+/////////////////////////////////////////////
+
+/*jshint esversion: 11 */
+
+const rendererMap = [
+ [[
+ /(intel).*\b(hd\sgraphics\s\d{4}|iris(?:\spro)|gma\s\w+)/i, // Intel
+ /(nvidia)\s(geforce\s(?:gtx?\s)\d\w+|quadro)/i, // NVIDIA
+ /\b(sis)\s(\w+)/i // SiS
+ ], ['vendor', 'model']],
+
+ [[
+ /\b(radeon[\shdr\d]+\w{4,5})/i // ATI/AMD
+ ], ['model', ['vendor', 'AMD']]],
+
+ [[
+ /(adreno\s(?:\(tm\)\s)\w+)/i // Qualcomm
+ ], [['model', /\(tm\)\s/i, ''], ['vendor', 'Qualcomm']]]
+];
+
+const vendorMap = [
+ [[
+ /\b(amd|apple|arm|ati|img|intel|nvidia|qualcomm|samsung|sis)\b/i
+ ], ['vendor']]
+];
+
+class RegexMap {
+
+ static parse(str, mapper) {
+ let res = {};
+ if (typeof str === 'string') {
+ for (const [regs, props] of mapper) {
+ if (!Array.isArray(regs)) {
+ throw new Error('RegexMap: Expect Array of RegExp');
+ }
+ if (!Array.isArray(props)) {
+ throw new Error('RegexMap: Expect Array for Properties Mapping');
+ }
+ for (const reg of regs) {
+ if (!reg instanceof RegExp) {
+ throw new Error('RegexMap: Expect RegExp Instance');
+ }
+ const matches = reg.exec(str);
+ if (matches) {
+ props.forEach((prop, idx) => {
+ const val = matches[idx+1];
+ if (Array.isArray(prop)) {
+ const key = prop[0];
+ if (typeof key !== 'string') {
+ throw new Error('RegexMap: Expect String Input');
+ }
+ if (prop.length == 2) {
+ if (typeof prop[1] === 'string') {
+ res[key] = prop[1];
+ } else if (typeof prop[1] === 'function') {
+ res[key] = prop[1].call(res, val);
+ }
+ } else if (prop.length == 3) {
+ if (prop[1] instanceof RegExp) {
+ res[key] = val.replace(prop[1], prop[2]);
+ } else if (typeof prop[1] === 'function') {
+ res[key] = prop[1].call(res, val, prop[2]);
+ }
+ } else if (prop.length == 4) {
+ res[key] = prop[3].call(res, val.replace(prop[1], prop[2]));
+ } else {
+ res[key] = val;
+ }
+ } else if (typeof prop === 'string') {
+ res[prop] = val;
+ }
+ });
+ if (res) return res;
+ }
+ }
+ }
+ }
+ return res;
+ };
+}
+
+class GPUDetect {
+
+ static getGPU (strRenderer, strVendor) {
+
+ let gpuInfo = { vendor : undefined, model : undefined };
+
+ if (typeof strRenderer !== 'string') {
+ if (globalThis.document) {
+ const canvas = document.createElement('canvas');
+ const gl = canvas.getContext('webgl2') ||
+ canvas.getContext('webgl') ||
+ canvas.getContext('experimental-webgl');
+ if (gl) {
+ const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
+ strVendor = gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL);
+ strRenderer = gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL);
+ }
+ }
+ }
+ if (strRenderer || strVendor) {
+ ({ vendor : gpuInfo.vendor, model : gpuInfo.model } = RegexMap.parse(strRenderer, rendererMap));
+ gpuInfo.vendor = gpuInfo.vendor ?? RegexMap.parse(strVendor, rendererMap)?.vendor ?? RegexMap.parse(strVendor, vendorMap)?.vendor;
+ }
+ return gpuInfo;
+ }
+}
+
+module.exports = {
+ GPUDetect
+}
\ No newline at end of file
diff --git a/src/gpu-detect/package.json b/src/gpu-detect/package.json
new file mode 100644
index 000000000..c3ffaf9f5
--- /dev/null
+++ b/src/gpu-detect/package.json
@@ -0,0 +1,25 @@
+{
+ "title": "User-Agent GPU Info",
+ "name": "@ua-parser-js/gpu-detect",
+ "version": "0.0.1",
+ "author": "Faisal Salman ",
+ "description": "Extracts GPU information from user-agent",
+ "main": "gpu-detect.js",
+ "module": "gpu-detect.mjs",
+ "scripts": {
+ "test": "mocha ../../test/mocha-test-helpers"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/faisalman/ua-parser-js.git"
+ },
+ "keywords": [
+ "ua-parser-js",
+ "gpu-detection"
+ ],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/faisalman/ua-parser-js/issues"
+ },
+ "homepage": "https://github.com/faisalman/ua-parser-js#readme"
+}
diff --git a/src/gpu-detect/readme.md b/src/gpu-detect/readme.md
new file mode 100644
index 000000000..86b9fa45f
--- /dev/null
+++ b/src/gpu-detect/readme.md
@@ -0,0 +1,16 @@
+# @ua-parser-js/gpu-detect
+
+This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that extracts GPU information from user-agent.
+
+```sh
+npm i @ua-parser-js/gpu-detect
+```
+
+## Code Example
+
+// in browser environment
+const { vendor, model } = GPUDetect.getGPU();
+
+// in non-browser environment
+const { vendor, model } = GPUDetect.getGPU("AMD Radeon");
+```
\ No newline at end of file
diff --git a/src/gpu-detect/test/index.js b/src/gpu-detect/test/index.js
new file mode 100644
index 000000000..76b7e7046
--- /dev/null
+++ b/src/gpu-detect/test/index.js
@@ -0,0 +1,4 @@
+const { GPUDetect } = require('../gpu-detect.js');
+
+console.log(GPUDetect.getGPU('AMD Radeon R9 M295X OpenGL Engine'));
+console.log(GPUDetect.getGPU('','ATI Technologies Inc.'));
\ No newline at end of file
From 647e115a1eff4aa65a60079ed18969bd23e67ffa Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 4 Sep 2023 12:28:11 +0700
Subject: [PATCH 123/388] [ua-helpers] Fix type error
---
src/user-agent-helpers/package.json | 4 +-
src/user-agent-helpers/readme.md | 10 ++++
.../user-agent-helpers/test/index.js | 56 ++-----------------
.../user-agent-helpers.d.ts | 35 +++++++++++-
4 files changed, 50 insertions(+), 55 deletions(-)
rename test/mocha-test-helpers.js => src/user-agent-helpers/test/index.js (62%)
diff --git a/src/user-agent-helpers/package.json b/src/user-agent-helpers/package.json
index aef3208e2..ba573084f 100644
--- a/src/user-agent-helpers/package.json
+++ b/src/user-agent-helpers/package.json
@@ -1,13 +1,13 @@
{
"title": "User-Agent Helpers",
"name": "@ua-parser-js/user-agent-helpers",
- "version": "0.0.1",
+ "version": "0.0.2",
"author": "Faisal Salman ",
"description": "A collection of utility methods for working with user-agent",
"main": "user-agent-helpers.js",
"module": "user-agent-helpers.mjs",
"scripts": {
- "test": "mocha ../../test/mocha-test-helpers"
+ "test": "mocha ./test/*"
},
"repository": {
"type": "git",
diff --git a/src/user-agent-helpers/readme.md b/src/user-agent-helpers/readme.md
index b2eefd25b..472070765 100644
--- a/src/user-agent-helpers/readme.md
+++ b/src/user-agent-helpers/readme.md
@@ -64,4 +64,14 @@ import { unfreezeUA } from '@ua-parser-js/user-agent-helpers';
unfreezeUA()
.then(newUA => console.log(newUA));
// 'Mozilla/5.0 (Windows NT 11.0; ARM) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
+
+/*
+// Alternatively:
+const ua = navigator.userAgent;
+const ch = await navigator.userAgentData.getHighEntropyValues();
+const newUA = await unfreezeUA(ua, ch);
+
+// Server environment:
+const newUA = await unfreezeUA(req.headers);
+*/
```
\ No newline at end of file
diff --git a/test/mocha-test-helpers.js b/src/user-agent-helpers/test/index.js
similarity index 62%
rename from test/mocha-test-helpers.js
rename to src/user-agent-helpers/test/index.js
index 2c0b39f05..4cea95409 100644
--- a/test/mocha-test-helpers.js
+++ b/src/user-agent-helpers/test/index.js
@@ -1,4 +1,4 @@
-const { isFrozenUA, unfreezeUA } = require('@ua-parser-js/user-agent-helpers');
+const { isFrozenUA, unfreezeUA } = require('../user-agent-helpers');
const { UAClientHints } = require('@ua-parser-js/ua-client-hints');
const assert = require('assert');
@@ -35,7 +35,8 @@ describe('isFrozenUA()', () => {
});
});
-const headers = {
+const req = {
+ headers : {
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
'sec-ch-ua-arch' : '"arm"',
@@ -45,58 +46,11 @@ const headers = {
'sec-ch-ua-platform' : '"Linux"',
'sec-ch-ua-platform-version' : '"13"',
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
-};
+}};
describe('unfreezeUA()', () => {
it('returns an unfreezed user-agent using real data from client hints HTTP headers (sec-ch-ua)', async () => {
- const unfreezed = await unfreezeUA(headers);
+ const unfreezed = await unfreezeUA(req.headers);
assert.strictEqual(unfreezed, 'Mozilla/5.0 (X11; Linux arm64) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/93.0.1.2 Chrome/93.0.1.2 Safari/537.36');
});
-});
-
-describe('Parse CH Headers', () => {
- it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => {
- assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(['architecture', 'bitness']), {
- "architecture": "arm",
- "bitness": "64"
- });
- assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(), {
- "architecture": "arm",
- "bitness": "64",
- "brands": [
- {
- "brand": "Chromium",
- "version": "93"
- },
- {
- "brand": "Google Chrome",
- "version": "93"
- },
- {
- "brand": "Not;A Brand",
- "version": "99"
- }
- ],
- "fullVersionList": [
- {
- "brand": "Chromium",
- "version": "93.0.1.2"
- },
- {
- "brand": "Google Chrome",
- "version": "93.0.1.2"
- },
- {
- "brand": "Not;A Brand",
- "version": "99.0.1.2"
- }
- ],
- "formFactor": null,
- "mobile": true,
- "model": "Pixel 99",
- "platform": "Linux",
- "platformVersion": "13",
- "wow64": null
- });
- });
});
\ No newline at end of file
diff --git a/src/user-agent-helpers/user-agent-helpers.d.ts b/src/user-agent-helpers/user-agent-helpers.d.ts
index ae63f9fe7..d77af20c8 100644
--- a/src/user-agent-helpers/user-agent-helpers.d.ts
+++ b/src/user-agent-helpers/user-agent-helpers.d.ts
@@ -1,4 +1,35 @@
+export type UABrowser = {
+ brand: string | null,
+ version: string | null
+};
+
+export type UADataType = boolean | string | Array | null;
+export type UADataField =
+ 'brands' |
+ 'mobile' |
+ 'platform' |
+ 'architecture' |
+ 'bitness' |
+ 'formFactor' |
+ 'fullVersionList' |
+ 'model' |
+ 'platformVersion' |
+ 'wow64';
+
+export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list';
+export type HeaderField =
+ 'sec-ch-ua-arch' |
+ 'sec-ch-ua-bitness' |
+ 'sec-ch-ua' |
+ 'sec-ch-ua-form-factor' |
+ 'sec-ch-ua-full-version-list' |
+ 'sec-ch-ua-mobile' |
+ 'sec-ch-ua-model' |
+ 'sec-ch-ua-platform' |
+ 'sec-ch-ua-platform-version' |
+ 'sec-ch-ua-wow64';
+
export function isFrozenUA(ua: string): boolean;
export function unfreezeUA(): Promise;
-export function unfreezeUA(ua: string, ch: ClientHintsJSHighEntropy): Promise;
-export function unfreezeUA(headers: ClientHintsHTTPHeaders): Promise;
\ No newline at end of file
+export function unfreezeUA(ua: string, ch: Record): Promise;
+export function unfreezeUA(headers: Record): Promise;
\ No newline at end of file
From a661ab61d5e06df2c423edc3c9256f304ce14d52 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 4 Sep 2023 22:41:58 +0700
Subject: [PATCH 124/388] [ua-client-hints] update test file
---
src/ua-client-hints/package.json | 2 +-
src/ua-client-hints/test/index.js | 64 +++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+), 1 deletion(-)
create mode 100644 src/ua-client-hints/test/index.js
diff --git a/src/ua-client-hints/package.json b/src/ua-client-hints/package.json
index 0ea28cdb9..9fdb8eaf7 100644
--- a/src/ua-client-hints/package.json
+++ b/src/ua-client-hints/package.json
@@ -7,7 +7,7 @@
"main": "ua-client-hints.js",
"module": "ua-client-hints.mjs",
"scripts": {
- "test": "mocha ../../test/mocha-test-helpers"
+ "test": "mocha ./test/*"
},
"repository": {
"type": "git",
diff --git a/src/ua-client-hints/test/index.js b/src/ua-client-hints/test/index.js
new file mode 100644
index 000000000..23d126245
--- /dev/null
+++ b/src/ua-client-hints/test/index.js
@@ -0,0 +1,64 @@
+const { UAClientHints } = require('../ua-client-hints');
+const assert = require('assert');
+
+describe('Parse CH Headers', () => {
+ it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => {
+
+ const req = {
+ headers : {
+ 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
+ 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
+ 'sec-ch-ua-arch' : '"arm"',
+ 'sec-ch-ua-bitness' : '"64"',
+ 'sec-ch-ua-mobile' : '?1',
+ 'sec-ch-ua-model' : '"Pixel 99"',
+ 'sec-ch-ua-platform' : '"Linux"',
+ 'sec-ch-ua-platform-version' : '"13"',
+ 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
+ }};
+
+ assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(['architecture', 'bitness']), {
+ "architecture": "arm",
+ "bitness": "64"
+ });
+
+ assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(), {
+ "architecture": "arm",
+ "bitness": "64",
+ "brands": [
+ {
+ "brand": "Chromium",
+ "version": "93"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93"
+ },
+ {
+ "brand": "Not;A Brand",
+ "version": "99"
+ }
+ ],
+ "fullVersionList": [
+ {
+ "brand": "Chromium",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": "Google Chrome",
+ "version": "93.0.1.2"
+ },
+ {
+ "brand": "Not;A Brand",
+ "version": "99.0.1.2"
+ }
+ ],
+ "formFactor": null,
+ "mobile": true,
+ "model": "Pixel 99",
+ "platform": "Linux",
+ "platformVersion": "13",
+ "wow64": null
+ });
+ });
+});
\ No newline at end of file
From 385e0aaee53e474899e7834d0e1d1926b5cc3591 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Mon, 4 Sep 2023 22:42:52 +0700
Subject: [PATCH 125/388] Regenerate lockfile
---
package-lock.json | 161 ++++++++++++++++++++++++++++++++++++++--------
1 file changed, 135 insertions(+), 26 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 4f5336d39..a3c6c31ce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,17 +23,16 @@
],
"license": "MIT",
"workspaces": [
+ "src/gpu-detect",
"src/ua-client-hints",
"src/user-agent-helpers"
],
- "dependencies": {
- "eslint": "^8.48.0"
- },
"devDependencies": {
"@babel/parser": "7.15.8",
"@babel/traverse": "7.15.4",
"@jazzer.js/core": "^1.4.0",
"@playwright/test": "~1.32.2",
+ "eslint": "^8.48.0",
"jshint": "~2.13.6",
"mocha": "~8.2.0",
"requirejs": "2.3.2",
@@ -48,6 +47,7 @@
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
"integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -469,6 +469,7 @@
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
"dependencies": {
"eslint-visitor-keys": "^3.3.0"
},
@@ -483,6 +484,7 @@
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz",
"integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==",
+ "dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
@@ -491,6 +493,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
"integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -512,12 +515,14 @@
"node_modules/@eslint/eslintrc/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "13.21.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
"integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+ "dev": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -532,6 +537,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
"dependencies": {
"argparse": "^2.0.1"
},
@@ -543,6 +549,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -554,6 +561,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
"engines": {
"node": ">=8"
},
@@ -565,6 +573,7 @@
"version": "8.48.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz",
"integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==",
+ "dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
@@ -573,6 +582,7 @@
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
"integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
+ "dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -586,6 +596,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
"engines": {
"node": ">=12.22"
},
@@ -597,7 +608,8 @@
"node_modules/@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
- "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
},
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
@@ -888,6 +900,7 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -900,6 +913,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
"engines": {
"node": ">= 8"
}
@@ -908,6 +922,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -945,6 +960,10 @@
"resolved": "src/client-hints-helpers",
"link": true
},
+ "node_modules/@ua-parser-js/gpu-detect": {
+ "resolved": "src/gpu-detect",
+ "link": true
+ },
"node_modules/@ua-parser-js/ua-client-hints": {
"resolved": "src/ua-client-hints",
"link": true
@@ -963,6 +982,7 @@
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -974,6 +994,7 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
@@ -982,6 +1003,7 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -1119,7 +1141,8 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
},
"node_modules/base64-js": {
"version": "1.5.1",
@@ -1197,6 +1220,7 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -1282,6 +1306,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
"engines": {
"node": ">=6"
}
@@ -1667,7 +1692,8 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
},
"node_modules/console-browserify": {
"version": "1.1.0",
@@ -1700,6 +1726,7 @@
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -1719,6 +1746,7 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -1767,7 +1795,8 @@
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
},
"node_modules/default-require-extensions": {
"version": "3.0.1",
@@ -1821,6 +1850,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
"dependencies": {
"esutils": "^2.0.2"
},
@@ -1933,6 +1963,7 @@
"version": "8.48.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz",
"integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==",
+ "dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -1986,6 +2017,7 @@
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -2001,6 +2033,7 @@
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -2012,6 +2045,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -2020,6 +2054,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -2033,12 +2068,14 @@
"node_modules/eslint/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
},
"node_modules/eslint/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -2054,6 +2091,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -2064,12 +2102,14 @@
"node_modules/eslint/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
"engines": {
"node": ">=10"
},
@@ -2081,6 +2121,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -2092,6 +2133,7 @@
"version": "13.21.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
"integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+ "dev": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -2106,6 +2148,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -2114,6 +2157,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
"dependencies": {
"argparse": "^2.0.1"
},
@@ -2125,6 +2169,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -2136,6 +2181,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -2147,6 +2193,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -2158,6 +2205,7 @@
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
"dependencies": {
"acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
@@ -2187,6 +2235,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
"dependencies": {
"estraverse": "^5.1.0"
},
@@ -2198,6 +2247,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
"dependencies": {
"estraverse": "^5.2.0"
},
@@ -2209,6 +2259,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
"engines": {
"node": ">=4.0"
}
@@ -2217,6 +2268,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2242,22 +2294,26 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
},
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
@@ -2266,6 +2322,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
"dependencies": {
"flat-cache": "^3.0.4"
},
@@ -2295,6 +2352,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -2319,6 +2377,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
"integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
"dependencies": {
"flatted": "^3.1.0",
"rimraf": "^3.0.2"
@@ -2330,7 +2389,8 @@
"node_modules/flatted": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
- "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.2",
@@ -2419,7 +2479,8 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
},
"node_modules/gauge": {
"version": "4.0.4",
@@ -2527,6 +2588,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -2558,6 +2620,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -2583,7 +2646,8 @@
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
},
"node_modules/growl": {
"version": "1.10.5",
@@ -2661,6 +2725,7 @@
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
"engines": {
"node": ">= 4"
}
@@ -2669,6 +2734,7 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -2684,6 +2750,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
"engines": {
"node": ">=0.8.19"
}
@@ -2692,6 +2759,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -2700,7 +2768,8 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
},
"node_modules/ini": {
"version": "1.3.8",
@@ -2724,6 +2793,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2741,6 +2811,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -2761,6 +2832,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -2783,7 +2855,8 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.0",
@@ -2922,12 +2995,14 @@
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
},
"node_modules/json5": {
"version": "2.2.3",
@@ -2957,6 +3032,7 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@@ -2969,6 +3045,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -2994,7 +3071,8 @@
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
},
"node_modules/log-symbols": {
"version": "4.0.0",
@@ -3171,6 +3249,7 @@
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
"integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -3399,7 +3478,8 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
},
"node_modules/napi-build-utils": {
"version": "1.0.2",
@@ -3410,7 +3490,8 @@
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
},
"node_modules/node-abi": {
"version": "3.40.0",
@@ -3503,6 +3584,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
"dependencies": {
"wrappy": "1"
}
@@ -3511,6 +3593,7 @@
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
"integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
"dependencies": {
"@aashutoshrathi/word-wrap": "^1.2.3",
"deep-is": "^0.1.3",
@@ -3527,6 +3610,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -3541,6 +3625,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -3564,6 +3649,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -3575,6 +3661,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -3583,6 +3670,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3591,6 +3679,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -3655,6 +3744,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
"engines": {
"node": ">= 0.8.0"
}
@@ -3690,6 +3780,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
"engines": {
"node": ">=6"
}
@@ -3698,6 +3789,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -3811,6 +3903,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
"engines": {
"node": ">=4"
}
@@ -3828,6 +3921,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@@ -3837,6 +3931,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -3851,6 +3946,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -3926,6 +4022,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@@ -3937,6 +4034,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -4164,7 +4262,8 @@
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
},
"node_modules/tmp": {
"version": "0.2.1",
@@ -4215,6 +4314,7 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
"dependencies": {
"prelude-ls": "^1.2.1"
},
@@ -4226,6 +4326,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
"engines": {
"node": ">=10"
},
@@ -4288,6 +4389,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
@@ -4308,6 +4410,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -4391,7 +4494,8 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
},
"node_modules/y18n": {
"version": "4.0.3",
@@ -4572,6 +4676,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
"engines": {
"node": ">=10"
},
@@ -4584,6 +4689,10 @@
"version": "0.0.1",
"license": "MIT"
},
+ "src/gpu-detect": {
+ "version": "0.0.1",
+ "license": "MIT"
+ },
"src/helpers": {
"name": "@ua-parser-js/helpers",
"version": "0.0.3",
@@ -4597,7 +4706,7 @@
},
"src/user-agent-helpers": {
"name": "@ua-parser-js/user-agent-helpers",
- "version": "0.0.1",
+ "version": "0.0.2",
"license": "MIT",
"dependencies": {
"@ua-parser-js/client-hints-helpers": "*"
From 52263613480470541a8f8084ac7e30dc8aaef468 Mon Sep 17 00:00:00 2001
From: Faisal Salman
Date: Sat, 9 Sep 2023 19:32:28 +0700
Subject: [PATCH 126/388] Remove sub-packages since they've been moved to their
own dedicated repo
https://github.com/faisalman/ua-client-hints-js
https://github.com/faisalman/gpu-detect-js
https://github.com/faisalman/ua-is-frozen
https://github.com/faisalman/re-parse-js
---
.eslintrc.json | 13 --
package.json | 6 -
script/build-module.js | 18 ---
script/test-all.sh | 2 +-
src/gpu-detect/gpu-detect.d.ts | 3 -
src/gpu-detect/gpu-detect.js | 116 ---------------
src/gpu-detect/package.json | 25 ----
src/gpu-detect/readme.md | 16 ---
src/gpu-detect/test/index.js | 4 -
src/ua-client-hints/package.json | 29 ----
src/ua-client-hints/readme.md | 128 -----------------
src/ua-client-hints/test/index.js | 64 ---------
src/ua-client-hints/ua-client-hints.d.ts | 39 -----
src/ua-client-hints/ua-client-hints.js | 130 -----------------
src/ua-client-hints/ua-client-hints.mjs | 134 ------------------
src/user-agent-helpers/package.json | 32 -----
src/user-agent-helpers/readme.md | 77 ----------
src/user-agent-helpers/test/index.js | 56 --------
.../user-agent-helpers.d.ts | 35 -----
src/user-agent-helpers/user-agent-helpers.js | 97 -------------
src/user-agent-helpers/user-agent-helpers.mjs | 101 -------------
21 files changed, 1 insertion(+), 1124 deletions(-)
delete mode 100644 .eslintrc.json
delete mode 100644 src/gpu-detect/gpu-detect.d.ts
delete mode 100644 src/gpu-detect/gpu-detect.js
delete mode 100644 src/gpu-detect/package.json
delete mode 100644 src/gpu-detect/readme.md
delete mode 100644 src/gpu-detect/test/index.js
delete mode 100644 src/ua-client-hints/package.json
delete mode 100644 src/ua-client-hints/readme.md
delete mode 100644 src/ua-client-hints/test/index.js
delete mode 100644 src/ua-client-hints/ua-client-hints.d.ts
delete mode 100644 src/ua-client-hints/ua-client-hints.js
delete mode 100644 src/ua-client-hints/ua-client-hints.mjs
delete mode 100644 src/user-agent-helpers/package.json
delete mode 100644 src/user-agent-helpers/readme.md
delete mode 100644 src/user-agent-helpers/test/index.js
delete mode 100644 src/user-agent-helpers/user-agent-helpers.d.ts
delete mode 100644 src/user-agent-helpers/user-agent-helpers.js
delete mode 100644 src/user-agent-helpers/user-agent-helpers.mjs
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 96aa29a4d..000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "env": {
- "browser": true,
- "commonjs": true,
- "es2021": true,
- "node": true
- },
- "parserOptions": {
- "ecmaVersion": "latest"
- },
- "rules": {
- }
-}
diff --git a/package.json b/package.json
index 1bff1d7f1..951a7f01d 100644
--- a/package.json
+++ b/package.json
@@ -182,7 +182,6 @@
"@babel/traverse": "7.15.4",
"@jazzer.js/core": "^1.4.0",
"@playwright/test": "~1.32.2",
- "eslint": "^8.48.0",
"jshint": "~2.13.6",
"mocha": "~8.2.0",
"requirejs": "2.3.2",
@@ -219,10 +218,5 @@
"type": "github",
"url": "https://github.com/sponsors/faisalman"
}
- ],
- "workspaces": [
- "src/gpu-detect",
- "src/ua-client-hints",
- "src/user-agent-helpers"
]
}
diff --git a/script/build-module.js b/script/build-module.js
index 2c0c4587d..f8079233c 100755
--- a/script/build-module.js
+++ b/script/build-module.js
@@ -46,24 +46,6 @@ const modules = [
dest : 'src/extensions/ua-parser-extensions.mjs',
title : 'ua-parser-js/extensions',
replacements : []
- },
- {
- src : 'src/gpu-detect/gpu-detect.js',
- dest : 'src/gpu-detect/gpu-detect.mjs',
- title : '@ua-parser-js/gpu-detect',
- replacements : []
- },
- {
- src : 'src/user-agent-helpers/user-agent-helpers.js',
- dest : 'src/user-agent-helpers/user-agent-helpers.mjs',
- title : '@ua-parser-js/user-agent-helpers',
- replacements : []
- },
- {
- src : 'src/ua-client-hints/ua-client-hints.js',
- dest : 'src/ua-client-hints/ua-client-hints.mjs',
- title : '@ua-parser-js/ua-client-hints',
- replacements : []
}
];
diff --git a/script/test-all.sh b/script/test-all.sh
index c8b2ca29b..37cbd19bf 100755
--- a/script/test-all.sh
+++ b/script/test-all.sh
@@ -9,7 +9,7 @@ echo '
- lint js code
'
npm run test:jshint || exit 1
-npm run test:eslint || exit 1
+#npm run test:eslint || exit 1
echo '
- test using mocha
diff --git a/src/gpu-detect/gpu-detect.d.ts b/src/gpu-detect/gpu-detect.d.ts
deleted file mode 100644
index bccc81b6c..000000000
--- a/src/gpu-detect/gpu-detect.d.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export class GPUDetect {
- static getGPU: { vendor: string, model: string }
-}
\ No newline at end of file
diff --git a/src/gpu-detect/gpu-detect.js b/src/gpu-detect/gpu-detect.js
deleted file mode 100644
index 326dbcf43..000000000
--- a/src/gpu-detect/gpu-detect.js
+++ /dev/null
@@ -1,116 +0,0 @@
-//////////////////////////////////////////////
-/* Extracts GPU information from user-agent
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-/////////////////////////////////////////////
-
-/*jshint esversion: 11 */
-
-const rendererMap = [
- [[
- /(intel).*\b(hd\sgraphics\s\d{4}|iris(?:\spro)|gma\s\w+)/i, // Intel
- /(nvidia)\s(geforce\s(?:gtx?\s)\d\w+|quadro)/i, // NVIDIA
- /\b(sis)\s(\w+)/i // SiS
- ], ['vendor', 'model']],
-
- [[
- /\b(radeon[\shdr\d]+\w{4,5})/i // ATI/AMD
- ], ['model', ['vendor', 'AMD']]],
-
- [[
- /(adreno\s(?:\(tm\)\s)\w+)/i // Qualcomm
- ], [['model', /\(tm\)\s/i, ''], ['vendor', 'Qualcomm']]]
-];
-
-const vendorMap = [
- [[
- /\b(amd|apple|arm|ati|img|intel|nvidia|qualcomm|samsung|sis)\b/i
- ], ['vendor']]
-];
-
-class RegexMap {
-
- static parse(str, mapper) {
- let res = {};
- if (typeof str === 'string') {
- for (const [regs, props] of mapper) {
- if (!Array.isArray(regs)) {
- throw new Error('RegexMap: Expect Array of RegExp');
- }
- if (!Array.isArray(props)) {
- throw new Error('RegexMap: Expect Array for Properties Mapping');
- }
- for (const reg of regs) {
- if (!reg instanceof RegExp) {
- throw new Error('RegexMap: Expect RegExp Instance');
- }
- const matches = reg.exec(str);
- if (matches) {
- props.forEach((prop, idx) => {
- const val = matches[idx+1];
- if (Array.isArray(prop)) {
- const key = prop[0];
- if (typeof key !== 'string') {
- throw new Error('RegexMap: Expect String Input');
- }
- if (prop.length == 2) {
- if (typeof prop[1] === 'string') {
- res[key] = prop[1];
- } else if (typeof prop[1] === 'function') {
- res[key] = prop[1].call(res, val);
- }
- } else if (prop.length == 3) {
- if (prop[1] instanceof RegExp) {
- res[key] = val.replace(prop[1], prop[2]);
- } else if (typeof prop[1] === 'function') {
- res[key] = prop[1].call(res, val, prop[2]);
- }
- } else if (prop.length == 4) {
- res[key] = prop[3].call(res, val.replace(prop[1], prop[2]));
- } else {
- res[key] = val;
- }
- } else if (typeof prop === 'string') {
- res[prop] = val;
- }
- });
- if (res) return res;
- }
- }
- }
- }
- return res;
- };
-}
-
-class GPUDetect {
-
- static getGPU (strRenderer, strVendor) {
-
- let gpuInfo = { vendor : undefined, model : undefined };
-
- if (typeof strRenderer !== 'string') {
- if (globalThis.document) {
- const canvas = document.createElement('canvas');
- const gl = canvas.getContext('webgl2') ||
- canvas.getContext('webgl') ||
- canvas.getContext('experimental-webgl');
- if (gl) {
- const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
- strVendor = gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL);
- strRenderer = gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL);
- }
- }
- }
- if (strRenderer || strVendor) {
- ({ vendor : gpuInfo.vendor, model : gpuInfo.model } = RegexMap.parse(strRenderer, rendererMap));
- gpuInfo.vendor = gpuInfo.vendor ?? RegexMap.parse(strVendor, rendererMap)?.vendor ?? RegexMap.parse(strVendor, vendorMap)?.vendor;
- }
- return gpuInfo;
- }
-}
-
-module.exports = {
- GPUDetect
-}
\ No newline at end of file
diff --git a/src/gpu-detect/package.json b/src/gpu-detect/package.json
deleted file mode 100644
index c3ffaf9f5..000000000
--- a/src/gpu-detect/package.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "title": "User-Agent GPU Info",
- "name": "@ua-parser-js/gpu-detect",
- "version": "0.0.1",
- "author": "Faisal Salman ",
- "description": "Extracts GPU information from user-agent",
- "main": "gpu-detect.js",
- "module": "gpu-detect.mjs",
- "scripts": {
- "test": "mocha ../../test/mocha-test-helpers"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/faisalman/ua-parser-js.git"
- },
- "keywords": [
- "ua-parser-js",
- "gpu-detection"
- ],
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/faisalman/ua-parser-js/issues"
- },
- "homepage": "https://github.com/faisalman/ua-parser-js#readme"
-}
diff --git a/src/gpu-detect/readme.md b/src/gpu-detect/readme.md
deleted file mode 100644
index 86b9fa45f..000000000
--- a/src/gpu-detect/readme.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# @ua-parser-js/gpu-detect
-
-This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that extracts GPU information from user-agent.
-
-```sh
-npm i @ua-parser-js/gpu-detect
-```
-
-## Code Example
-
-// in browser environment
-const { vendor, model } = GPUDetect.getGPU();
-
-// in non-browser environment
-const { vendor, model } = GPUDetect.getGPU("AMD Radeon");
-```
\ No newline at end of file
diff --git a/src/gpu-detect/test/index.js b/src/gpu-detect/test/index.js
deleted file mode 100644
index 76b7e7046..000000000
--- a/src/gpu-detect/test/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-const { GPUDetect } = require('../gpu-detect.js');
-
-console.log(GPUDetect.getGPU('AMD Radeon R9 M295X OpenGL Engine'));
-console.log(GPUDetect.getGPU('','ATI Technologies Inc.'));
\ No newline at end of file
diff --git a/src/ua-client-hints/package.json b/src/ua-client-hints/package.json
deleted file mode 100644
index 9fdb8eaf7..000000000
--- a/src/ua-client-hints/package.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "title": "User-Agent Client Hints",
- "name": "@ua-parser-js/ua-client-hints",
- "version": "0.0.1",
- "author": "Faisal Salman ",
- "description": "A collection of utility methods for working with user-agent client hints",
- "main": "ua-client-hints.js",
- "module": "ua-client-hints.mjs",
- "scripts": {
- "test": "mocha ./test/*"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/faisalman/ua-parser-js.git"
- },
- "keywords": [
- "ua-parser-js",
- "browser-detection",
- "device-detection",
- "os-detection",
- "user-agent",
- "client-hints"
- ],
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/faisalman/ua-parser-js/issues"
- },
- "homepage": "https://github.com/faisalman/ua-parser-js#readme"
-}
diff --git a/src/ua-client-hints/readme.md b/src/ua-client-hints/readme.md
deleted file mode 100644
index 253f4ebe7..000000000
--- a/src/ua-client-hints/readme.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# @ua-parser-js/ua-client-hints
-
-This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client hints.
-
-```sh
-npm i @ua-parser-js/ua-client-hints
-```
-
-### * `getUAData([props:array]):object`
-
-Get user-agent client hints values of current instance in form of JS object representation
-
-### * `setUAData([uaData:object]):UAClientHints`
-
-Set values of user-agent client hints for the current instance either from navigator.userAgentData or from HTTP headers (Sec-CH-UA-*)
-
-### * `getSerializedUAData([props:array]):object`
-
-Get user-agent client hints values of current instance in form of HTTP headers string representation (Sec-CH-UA-*)
-
-## Code Example
-
-```js
-import { UAClientHints } from '@ua-parser-js/ua-client-hints';
-
-/*
- Suppose we're in a server having this client hints data:
-
- const httpHeaders = {
- 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
- 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : 'arm',
- 'sec-ch-ua-bitness' : '64',
- 'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : 'Pixel 99',
- 'sec-ch-ua-platform' : 'Linux',
- 'sec-ch-ua-platform-version' : '13'
- };
-*/
-
-const uaCH = new UAClientHints();
-uaCH.setUAData(httpHeaders);
-const uaCHData1 = uaCH.getUAData();
-const uaCHData2 = uaCH.getUAData(['architecture', 'bitness']);
-
-console.log(uaCHData1);
-/*
- {
- "architecture": "arm",
- "bitness": "64",
- "brands": [
- {
- "brand": "Chromium",
- "version": "93"
- },
- {
- "brand": "Google Chrome",
- "version": "93"
- },
- {
- "brand": " Not;A Brand",
- "version": "99"
- }
- ],
- "fullVersionList": [
- {
- "brand": "Chromium",
- "version": "93.0.1.2"
- },
- {
- "brand": "Google Chrome",
- "version": "93.0.1.2"
- },
- {
- "brand": " Not;A Brand",
- "version": "99.0.1.2"
- }
- ],
- "mobile": true,
- "model": "Pixel 99",
- "platform": "Linux",
- "platformVersion": "13",
- "wow64": null,
- "formFactor": null
- }
-*/
-
-console.log(uaCHData2);
-/*
- {
- "architecture": "arm",
- "bitness": "64"
- }
-*/
-
-uaCH.setUAData({
- "wow64" : true,
- "formFactor" : "Automotive"
-});
-
-const headersData1 = uaCH.getSerializedUAData();
-const headersData2 = uaCH.getSerializedUAData(['brand', 'mobile', 'model']);
-
-console.log(headersData1);
-/*
- {
- 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
- 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : 'arm',
- 'sec-ch-ua-bitness' : '64',
- 'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : 'Pixel 99',
- 'sec-ch-ua-platform' : 'Linux',
- 'sec-ch-ua-platform-version' : '13',
- 'sec-ch-ua-wow64' : '?1',
- 'sec-ch-ua-form-factor' : 'Automotive'
- };
-*/
-
-console.log(headersData2);
-/*
- {
- 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
- 'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : 'Pixel 99'
- };
-*/
-```
\ No newline at end of file
diff --git a/src/ua-client-hints/test/index.js b/src/ua-client-hints/test/index.js
deleted file mode 100644
index 23d126245..000000000
--- a/src/ua-client-hints/test/index.js
+++ /dev/null
@@ -1,64 +0,0 @@
-const { UAClientHints } = require('../ua-client-hints');
-const assert = require('assert');
-
-describe('Parse CH Headers', () => {
- it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => {
-
- const req = {
- headers : {
- 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
- 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : '"arm"',
- 'sec-ch-ua-bitness' : '"64"',
- 'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : '"Pixel 99"',
- 'sec-ch-ua-platform' : '"Linux"',
- 'sec-ch-ua-platform-version' : '"13"',
- 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
- }};
-
- assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(['architecture', 'bitness']), {
- "architecture": "arm",
- "bitness": "64"
- });
-
- assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(), {
- "architecture": "arm",
- "bitness": "64",
- "brands": [
- {
- "brand": "Chromium",
- "version": "93"
- },
- {
- "brand": "Google Chrome",
- "version": "93"
- },
- {
- "brand": "Not;A Brand",
- "version": "99"
- }
- ],
- "fullVersionList": [
- {
- "brand": "Chromium",
- "version": "93.0.1.2"
- },
- {
- "brand": "Google Chrome",
- "version": "93.0.1.2"
- },
- {
- "brand": "Not;A Brand",
- "version": "99.0.1.2"
- }
- ],
- "formFactor": null,
- "mobile": true,
- "model": "Pixel 99",
- "platform": "Linux",
- "platformVersion": "13",
- "wow64": null
- });
- });
-});
\ No newline at end of file
diff --git a/src/ua-client-hints/ua-client-hints.d.ts b/src/ua-client-hints/ua-client-hints.d.ts
deleted file mode 100644
index 307765848..000000000
--- a/src/ua-client-hints/ua-client-hints.d.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-export type UABrowser = {
- brand: string | null,
- version: string | null
-};
-
-export type UADataType = boolean | string | Array | null;
-export type UADataField =
- 'brands' |
- 'mobile' |
- 'platform' |
- 'architecture' |
- 'bitness' |
- 'formFactor' |
- 'fullVersionList' |
- 'model' |
- 'platformVersion' |
- 'wow64';
-
-export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list';
-export type HeaderField =
- 'sec-ch-ua-arch' |
- 'sec-ch-ua-bitness' |
- 'sec-ch-ua' |
- 'sec-ch-ua-form-factor' |
- 'sec-ch-ua-full-version-list' |
- 'sec-ch-ua-mobile' |
- 'sec-ch-ua-model' |
- 'sec-ch-ua-platform' |
- 'sec-ch-ua-platform-version' |
- 'sec-ch-ua-wow64';
-
-export class UAClientHints {
- #ch: Map;
- #parseHeader(str: string, type: HeaderType): UADataType;
- #serialize(data: UADataType, type: HeaderType): string;
- getSerializedUAData(): Record;
- getUAData(props?: Array): Record;
- setUAData(uaData: Record | Record): UAClientHints;
-};
\ No newline at end of file
diff --git a/src/ua-client-hints/ua-client-hints.js b/src/ua-client-hints/ua-client-hints.js
deleted file mode 100644
index d4ddc6077..000000000
--- a/src/ua-client-hints/ua-client-hints.js
+++ /dev/null
@@ -1,130 +0,0 @@
-//////////////////////////////////////////////
-/* A collection of utility methods for
- working with user-agent client hints
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-/////////////////////////////////////////////
-
-/*jshint esversion: 11 */
-
-const fieldType = Object.freeze({
- Boolean : 'sf-boolean',
- List : 'sf-list',
- String : 'sf-string'
-});
-
-const uaCHMap = Object.freeze({
- architecture : {
- field : 'Sec-CH-UA-Arch',
- type : fieldType.String
- },
- bitness : {
- field : 'Sec-CH-UA-Bitness',
- type : fieldType.String
- },
- brands : {
- field : 'Sec-CH-UA',
- type : fieldType.List
- },
- formFactor : {
- field : 'Sec-CH-UA-Form-Factor',
- type : fieldType.String
- },
- fullVersionList : {
- field : 'Sec-CH-UA-Full-Version-List',
- type : fieldType.List
- },
- mobile : {
- field : 'Sec-CH-UA-Mobile',
- type : fieldType.Boolean
- },
- model : {
- field : 'Sec-CH-UA-Model',
- type : fieldType.String
- },
- platform : {
- field : 'Sec-CH-UA-Platform',
- type : fieldType.String
- },
- platformVersion : {
- field : 'Sec-CH-UA-Platform-Version',
- type : fieldType.String
- },
- wow64 : {
- field : 'Sec-CH-UA-WOW64',
- type : fieldType.Boolean
- }
-});
-
-class UAClientHints {
-
- #uach = new Map();
-
- constructor () {
- for (const key in uaCHMap) {
- this.#uach.set(key, null);
- }
- return this;
- };
-
- #parseHeader (str, type) {
- if (!str) {
- return null;
- }
- switch (type) {
- case fieldType.Boolean:
- return /\?1/.test(str);
- case fieldType.List:
- return str.replace(/\\?\"/g, '')
- .split(',')
- .map(brands => {
- const [brand, version] = brands.trim().split(';v=');
- return {
- brand : brand,
- version : version
- };
- });
- case fieldType.String:
- return str.replace(/\s*\\?\"\s*/g, '');
- default:
- return '';
- }
- };
-
- #serialize(data, type) {
- throw new Error('Not implemented yet');
- //return '';
- }
-
- getSerializedUAData() {
- throw new Error('Not implemented yet');
- //let http = {};
- //return http;
- }
-
- getUAData(props) {
- if (props) {
- return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)]));
- }
- return Object.fromEntries(this.#uach);
- }
-
- setUAData(uaDataValues) {
- if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) {
- for (const val in uaCHMap) {
- const { field, type } = uaCHMap[val];
- this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type));
- }
- } else {
- for (const value in uaDataValues) {
- if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]);
- }
- }
- return this;
- };
-}
-
-module.exports = {
- UAClientHints
-};
\ No newline at end of file
diff --git a/src/ua-client-hints/ua-client-hints.mjs b/src/ua-client-hints/ua-client-hints.mjs
deleted file mode 100644
index 047f5c828..000000000
--- a/src/ua-client-hints/ua-client-hints.mjs
+++ /dev/null
@@ -1,134 +0,0 @@
-// Generated ESM version of @ua-parser-js/ua-client-hints
-// DO NOT EDIT THIS FILE!
-// Source: /src/ua-client-hints/ua-client-hints.js
-
-//////////////////////////////////////////////
-/* A collection of utility methods for
- working with user-agent client hints
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-/////////////////////////////////////////////
-
-/*jshint esversion: 11 */
-
-const fieldType = Object.freeze({
- Boolean : 'sf-boolean',
- List : 'sf-list',
- String : 'sf-string'
-});
-
-const uaCHMap = Object.freeze({
- architecture : {
- field : 'Sec-CH-UA-Arch',
- type : fieldType.String
- },
- bitness : {
- field : 'Sec-CH-UA-Bitness',
- type : fieldType.String
- },
- brands : {
- field : 'Sec-CH-UA',
- type : fieldType.List
- },
- formFactor : {
- field : 'Sec-CH-UA-Form-Factor',
- type : fieldType.String
- },
- fullVersionList : {
- field : 'Sec-CH-UA-Full-Version-List',
- type : fieldType.List
- },
- mobile : {
- field : 'Sec-CH-UA-Mobile',
- type : fieldType.Boolean
- },
- model : {
- field : 'Sec-CH-UA-Model',
- type : fieldType.String
- },
- platform : {
- field : 'Sec-CH-UA-Platform',
- type : fieldType.String
- },
- platformVersion : {
- field : 'Sec-CH-UA-Platform-Version',
- type : fieldType.String
- },
- wow64 : {
- field : 'Sec-CH-UA-WOW64',
- type : fieldType.Boolean
- }
-});
-
-class UAClientHints {
-
- #uach = new Map();
-
- constructor () {
- for (const key in uaCHMap) {
- this.#uach.set(key, null);
- }
- return this;
- };
-
- #parseHeader (str, type) {
- if (!str) {
- return null;
- }
- switch (type) {
- case fieldType.Boolean:
- return /\?1/.test(str);
- case fieldType.List:
- return str.replace(/\\?\"/g, '')
- .split(',')
- .map(brands => {
- const [brand, version] = brands.trim().split(';v=');
- return {
- brand : brand,
- version : version
- };
- });
- case fieldType.String:
- return str.replace(/\s*\\?\"\s*/g, '');
- default:
- return '';
- }
- };
-
- #serialize(data, type) {
- throw new Error('Not implemented yet');
- //return '';
- }
-
- getSerializedUAData() {
- throw new Error('Not implemented yet');
- //let http = {};
- //return http;
- }
-
- getUAData(props) {
- if (props) {
- return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)]));
- }
- return Object.fromEntries(this.#uach);
- }
-
- setUAData(uaDataValues) {
- if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) {
- for (const val in uaCHMap) {
- const { field, type } = uaCHMap[val];
- this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type));
- }
- } else {
- for (const value in uaDataValues) {
- if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]);
- }
- }
- return this;
- };
-}
-
-export {
- UAClientHints
-};
\ No newline at end of file
diff --git a/src/user-agent-helpers/package.json b/src/user-agent-helpers/package.json
deleted file mode 100644
index ba573084f..000000000
--- a/src/user-agent-helpers/package.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "title": "User-Agent Helpers",
- "name": "@ua-parser-js/user-agent-helpers",
- "version": "0.0.2",
- "author": "Faisal Salman ",
- "description": "A collection of utility methods for working with user-agent",
- "main": "user-agent-helpers.js",
- "module": "user-agent-helpers.mjs",
- "scripts": {
- "test": "mocha ./test/*"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/faisalman/ua-parser-js.git"
- },
- "keywords": [
- "ua-parser-js",
- "browser-detection",
- "device-detection",
- "os-detection",
- "user-agent",
- "client-hints"
- ],
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/faisalman/ua-parser-js/issues"
- },
- "homepage": "https://github.com/faisalman/ua-parser-js#readme",
- "dependencies": {
- "@ua-parser-js/client-hints-helpers": "*"
- }
-}
diff --git a/src/user-agent-helpers/readme.md b/src/user-agent-helpers/readme.md
deleted file mode 100644
index 472070765..000000000
--- a/src/user-agent-helpers/readme.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# @ua-parser-js/user-agent-helpers
-
-This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent.
-
-```sh
-npm i @ua-parser-js/user-agent-helpers
-```
-
-### * `isFrozenUA(ua:string):boolean`
-
-Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/)
-
-### * `unfreezeUA([ua:string,ch:object]|[headers:object]):Promise`
-
-Construct new unfreezed user-agent string using real data from client hints
-
-## Code Example
-
-```js
-import { isFrozenUA } from '@ua-parser-js/user-agent-helpers';
-
-const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
-const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
-
-console.log(isFrozenUA(regularMobileUA));
-// false
-
-console.log(isFrozenUA(freezedMobileUA));
-// true
-```
-
-```js
-import { unfreezeUA } from '@ua-parser-js/user-agent-helpers';
-
-/*
- Suppose we're in a browser having this client hints data:
-
- {
- fullVersionList: [
- {
- brand: 'New Browser',
- version: '110.1.2.3'
- },
- {
- brand: 'Chromium',
- version: '110.1.2.3'
- },
- {
- brand: 'Not(A:Brand',
- version: '110'
- }
- ],
- platform: 'Windows',
- platformVersion: '13.0.0',
- architecture: 'arm'
- }
-
- With a frozen user-agent:
-
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
-*/
-
-// Now let's generate a complete user-agent:
-unfreezeUA()
- .then(newUA => console.log(newUA));
-// 'Mozilla/5.0 (Windows NT 11.0; ARM) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
-
-/*
-// Alternatively:
-const ua = navigator.userAgent;
-const ch = await navigator.userAgentData.getHighEntropyValues();
-const newUA = await unfreezeUA(ua, ch);
-
-// Server environment:
-const newUA = await unfreezeUA(req.headers);
-*/
-```
\ No newline at end of file
diff --git a/src/user-agent-helpers/test/index.js b/src/user-agent-helpers/test/index.js
deleted file mode 100644
index 4cea95409..000000000
--- a/src/user-agent-helpers/test/index.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const { isFrozenUA, unfreezeUA } = require('../user-agent-helpers');
-const { UAClientHints } = require('@ua-parser-js/ua-client-hints');
-const assert = require('assert');
-
-describe('isFrozenUA()', () => {
- it('Returns whether a user agent is frozen', () => {
-
- const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
- const freezedWindowsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
-
- const regularMacUA = "";
- const freezedMacUA = "";
-
- const regularLinuxUA = "";
- const freezedLinuxUA = "";
-
- const regularCrOSUA = "";
- const freezedCrOSUA = "";
-
- const regularFuchsiaUA = "";
- const freezedFuchsiaUA = "";
-
- const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
- const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
-
- const regularTabletUA = "Mozilla/5.0 (Linux; Android 9; SM-T810) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
- const freezedTabletUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
-
- assert.strictEqual(isFrozenUA(regularWindowsUA), false);
- assert.strictEqual(isFrozenUA(freezedWindowsUA), true);
- assert.strictEqual(isFrozenUA(regularMobileUA), false);
- assert.strictEqual(isFrozenUA(freezedMobileUA), true);
- assert.strictEqual(isFrozenUA(regularTabletUA), false);
- assert.strictEqual(isFrozenUA(freezedTabletUA), true);
- });
-});
-
-const req = {
- headers : {
- 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
- 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
- 'sec-ch-ua-arch' : '"arm"',
- 'sec-ch-ua-bitness' : '"64"',
- 'sec-ch-ua-mobile' : '?1',
- 'sec-ch-ua-model' : '"Pixel 99"',
- 'sec-ch-ua-platform' : '"Linux"',
- 'sec-ch-ua-platform-version' : '"13"',
- 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
-}};
-
-describe('unfreezeUA()', () => {
- it('returns an unfreezed user-agent using real data from client hints HTTP headers (sec-ch-ua)', async () => {
- const unfreezed = await unfreezeUA(req.headers);
- assert.strictEqual(unfreezed, 'Mozilla/5.0 (X11; Linux arm64) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/93.0.1.2 Chrome/93.0.1.2 Safari/537.36');
- });
-});
\ No newline at end of file
diff --git a/src/user-agent-helpers/user-agent-helpers.d.ts b/src/user-agent-helpers/user-agent-helpers.d.ts
deleted file mode 100644
index d77af20c8..000000000
--- a/src/user-agent-helpers/user-agent-helpers.d.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-export type UABrowser = {
- brand: string | null,
- version: string | null
-};
-
-export type UADataType = boolean | string | Array | null;
-export type UADataField =
- 'brands' |
- 'mobile' |
- 'platform' |
- 'architecture' |
- 'bitness' |
- 'formFactor' |
- 'fullVersionList' |
- 'model' |
- 'platformVersion' |
- 'wow64';
-
-export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list';
-export type HeaderField =
- 'sec-ch-ua-arch' |
- 'sec-ch-ua-bitness' |
- 'sec-ch-ua' |
- 'sec-ch-ua-form-factor' |
- 'sec-ch-ua-full-version-list' |
- 'sec-ch-ua-mobile' |
- 'sec-ch-ua-model' |
- 'sec-ch-ua-platform' |
- 'sec-ch-ua-platform-version' |
- 'sec-ch-ua-wow64';
-
-export function isFrozenUA(ua: string): boolean;
-export function unfreezeUA(): Promise;
-export function unfreezeUA(ua: string, ch: Record): Promise;
-export function unfreezeUA(headers: Record): Promise;
\ No newline at end of file
diff --git a/src/user-agent-helpers/user-agent-helpers.js b/src/user-agent-helpers/user-agent-helpers.js
deleted file mode 100644
index cfc9ecf07..000000000
--- a/src/user-agent-helpers/user-agent-helpers.js
+++ /dev/null
@@ -1,97 +0,0 @@
-////////////////////////////////////////////////////
-/* A collection of utility methods for user-agent
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-///////////////////////////////////////////////////
-
-/*jshint esversion: 11 */
-
-const { UAClientHints } = require('@ua-parser-js/ua-client-hints');
-
-/*
- # Reference:
- https://www.chromium.org/updates/ua-reduction/
-
- # Desktop
- ---
- Format:
- Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
-
- Possible values:
- - Windows NT 10.0; Win64; x64
- - Macintosh; Intel Mac OS X 10_15_7
- - X11; Linux x86_64
- - X11; CrOS x86_64 14541.0.0
- - Fuchsia
-
- # Mobile & Tablet: (except iOS/Android WebView)
- ---
- Format:
- Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
-
- Possible values:
- - "Mobile"
- - "" (empty string for Tablets & Desktop)
-*/
-
-const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua);
-
-const unfreezeUA = async (ua, ch) => {
- const env = typeof navigator == 'undefined' ? 'node' : 'browser';
- if (env == 'node') {
- if (!ua['user-agent']) {
- throw new Error('User-Agent header not found');
- }
- ch = new UAClientHints().setUAData(ua).getUAData();
- ua = ua['user-agent'];
- } else {
- ua = ua || navigator.userAgent;
- ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
- }
- if (isFrozenUA(ua) && ch) {
- switch (ch.platform) {
- case 'Windows':
- let [major, minor] = ch.platformVersion
- .split('.')
- .map(num => parseInt(num, 10));
- major = (major < 1) ? '6' : (major >= 13) ? '11' : '10';
- ua = ua .replace(/(?Windows NT) 10\.0/, `$ ${major}.${minor}`)
- .replace(/; (?Win64; x64)/,
- (ch.architecture == 'arm') ?
- '; ARM' :
- (ch.wow64) ?
- '; WOW64' :
- (ch.architecture == 'x86' && ch.bitness != '64') ?
- '' : '; $');
- break;
- case 'Android':
- ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`);
- break;
- case 'Linux':
- case 'Chrome OS':
- ua = ua.replace(/(?x86_64)/,
- (ch.architecture == 'arm') ?
- ((ch.bitness == '64') ? 'arm64' : 'arm') :
- (ch.architecture == 'x86' && ch.bitness != '64') ?
- 'x86' : '$');
- break;
- case 'macOS':
- ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`);
- break;
- }
- if (ch.fullVersionList) {
- ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /,
- ch.fullVersionList
- .filter(browser => !/not.a.brand/i.test(browser.brand))
- .map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `)
- .join(''));
- }
- }
- return ua;
-};
-
-module.exports = {
- isFrozenUA,
- unfreezeUA
-};
\ No newline at end of file
diff --git a/src/user-agent-helpers/user-agent-helpers.mjs b/src/user-agent-helpers/user-agent-helpers.mjs
deleted file mode 100644
index b8f398e09..000000000
--- a/src/user-agent-helpers/user-agent-helpers.mjs
+++ /dev/null
@@ -1,101 +0,0 @@
-// Generated ESM version of @ua-parser-js/user-agent-helpers
-// DO NOT EDIT THIS FILE!
-// Source: /src/user-agent-helpers/user-agent-helpers.js
-
-////////////////////////////////////////////////////
-/* A collection of utility methods for user-agent
- https://github.com/faisalman/ua-parser-js
- Author: Faisal Salman
- MIT License */
-///////////////////////////////////////////////////
-
-/*jshint esversion: 11 */
-
-import { UAClientHints } from '@ua-parser-js/ua-client-hints';
-
-/*
- # Reference:
- https://www.chromium.org/updates/ua-reduction/
-
- # Desktop
- ---
- Format:
- Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
-
- Possible values:
- - Windows NT 10.0; Win64; x64
- - Macintosh; Intel Mac OS X 10_15_7
- - X11; Linux x86_64
- - X11; CrOS x86_64 14541.0.0
- - Fuchsia
-
- # Mobile & Tablet: (except iOS/Android WebView)
- ---
- Format:
- Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36
-
- Possible values:
- - "Mobile"
- - "" (empty string for Tablets & Desktop)
-*/
-
-const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua);
-
-const unfreezeUA = async (ua, ch) => {
- const env = typeof navigator == 'undefined' ? 'node' : 'browser';
- if (env == 'node') {
- if (!ua['user-agent']) {
- throw new Error('User-Agent header not found');
- }
- ch = new UAClientHints().setUAData(ua).getUAData();
- ua = ua['user-agent'];
- } else {
- ua = ua || navigator.userAgent;
- ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
- }
- if (isFrozenUA(ua) && ch) {
- switch (ch.platform) {
- case 'Windows':
- let [major, minor] = ch.platformVersion
- .split('.')
- .map(num => parseInt(num, 10));
- major = (major < 1) ? '6' : (major >= 13) ? '11' : '10';
- ua = ua .replace(/(?Windows NT) 10\.0/, `$