From cd3bef74058d6428033606b286a45c59480c6356 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 27 Mar 2025 10:25:44 +0700 Subject: [PATCH 01/30] Improve OS detection: HarmonyOS, MorphOS --- src/main/ua-parser.js | 9 ++++--- test/data/ua/os/harmonyos.json | 47 +++++++++++++++++++++++++++++++++- test/data/ua/os/morphos.json | 27 +++++++++++++++++++ 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a6f1091..5c4cc78 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -924,7 +924,7 @@ /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, - /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS + /(macintosh|mac_powerpc\b)(?!.+(haiku|morphos))/i // Mac OS ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ // Google Chromecast @@ -940,10 +940,11 @@ ], [VERSION, [NAME, CHROMECAST]], [ // Mobile OSes - /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS + /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86 ], [VERSION, NAME], [ /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ @@ -979,7 +980,7 @@ /(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)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux + /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS /(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 @@ -988,7 +989,7 @@ ], [[NAME, 'Solaris'], VERSION], [ /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS + /\b(beos|os\/2|amigaos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/Fuchsia/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] diff --git a/test/data/ua/os/harmonyos.json b/test/data/ua/os/harmonyos.json index 1fa13ef..c201654 100644 --- a/test/data/ua/os/harmonyos.json +++ b/test/data/ua/os/harmonyos.json @@ -5,7 +5,52 @@ "expect" : { "name" : "HarmonyOS", - "version" : "10" + "version" : "undefined" + } + }, + { + "desc" : "HarmonyOS 2", + "ua" : "Mozilla/5.0 (Linux; Android 10; STK-AL00 Build/HUAWEISTK-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Mobile Safari/537.36 BDOS/1.0 (HarmonyOS 2.2.0) SP-engine/2.72.0 baiduboxapp/13.34.5.10 (Baidu; P1 10) NABar/1.0", + "expect" : + { + "name" : "HarmonyOS", + "version" : "2.2.0" + } + }, + { + "desc" : "HarmonyOS 3", + "ua" : "Mozilla/5.0 (Linux; Android 12; Huawei P60 Pro Build/HarmonyOS 3.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.1.6136.139 Mobile Safari/537.36 OPR/111.11", + "expect" : + { + "name" : "HarmonyOS", + "version" : "3.1.0" + } + }, + { + "desc" : "HarmonyOS 4", + "ua" : "Mozilla/5.0 (Android 12; HarmonyOS 4.0.0.118; OXF-AN00 Build/HUAWEIOXF-AN00; HMSCore 6.13.0.302; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36 EdgA/125.0.0.0 Language/zh_TW ABI/arm64-v8a", + "expect" : + { + "name" : "HarmonyOS", + "version" : "4.0.0.118" + } + }, + { + "desc" : "HarmonyOS 4", + "ua" : "Mozilla/5.0 (Linux; Android 12; HarmonyOS/4.0.3.601; JAD-AL50; HMSCore/6.13.0.320 Build/AP31.240223.016.A3; x64; ARM64) AppleWebKit/537.36 (KHTML, like Gecko) HuaweiBrowser/14.0.7.302 Mobile Safari/537.36", + "expect" : + { + "name" : "HarmonyOS", + "version" : "4.0.3.601" + } + }, + { + "desc" : "HarmonyOS 5", + "ua" : "Mozilla/5.0 (PC; OpenHarmony 5.0; HarmonyOS 5.0) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Browser/harmony360Browser/1.0.0", + "expect" : + { + "name" : "HarmonyOS", + "version" : "5.0" } } ] \ No newline at end of file diff --git a/test/data/ua/os/morphos.json b/test/data/ua/os/morphos.json index f255eeb..e540858 100644 --- a/test/data/ua/os/morphos.json +++ b/test/data/ua/os/morphos.json @@ -7,5 +7,32 @@ "name" : "MorphOS", "version" : "undefined" } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (Macintosh; PowerPC MorphOS 3.7; Odyssey Web Browser; rv:1.23) AppleWebKit/538.1 (KHTML, like Gecko) OWB/1.23 Safari/538.1", + "expect" : + { + "name" : "MorphOS", + "version" : "3.7" + } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (X11; MorphOS ppc64; rv:88.0) Gecko/20100101 Firefox/88.0", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (compatible; Origyn Web Browser; MorphOS; PPC; U) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+)", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } } ] \ No newline at end of file From 8ae88b6aa56eed2c6e0e0a0e30ffa14e2526494c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 28 Mar 2025 05:00:32 +0700 Subject: [PATCH 02/30] Improve OS detection: AIX, Fuchsia, Haiku, Solaris --- src/main/ua-parser.js | 12 ++++++------ test/data/ua/os/aix.json | 27 +++++++++++++++++++++++++++ test/data/ua/os/fuchsia.json | 9 +++++++++ test/data/ua/os/haiku.json | 27 +++++++++++++++++++++++++++ test/data/ua/os/solaris.json | 29 ++++++++++++++++++++++++++++- 5 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 5c4cc78..d3bd002 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -977,19 +977,19 @@ // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux + /(mageia|vectorlinux|fuchsia)[; ]/i, // Mageia/VectorLinux/Fuchsia /([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 + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris + /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS /(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 + /(haiku) ?(r\d)?/i // Haiku ], [NAME, VERSION], [ - /(sunos) ?([\w\.\d]*)/i // Solaris + /(sunos) ?([\d\.]*)/i // Solaris ], [[NAME, 'Solaris'], VERSION], [ - /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris - /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/Fuchsia/HP-UX/SerenityOS + /\b(beos|os\/2|amigaos|openvms|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] diff --git a/test/data/ua/os/aix.json b/test/data/ua/os/aix.json index 02ccda8..d45d1cb 100644 --- a/test/data/ua/os/aix.json +++ b/test/data/ua/os/aix.json @@ -7,5 +7,32 @@ "name" : "AIX", "version" : "undefined" } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (AIX; SPARC64; smart-thumbtack; Mosaic/2.7) Gecko/20100101 Mosaic/2.7", + "expect" : + { + "name" : "AIX", + "version" : "undefined" + } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; N; Linux i686; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Mozilla/3.0 (X11; I; AIX 2) Navigator/9.0.0.6", + "expect" : + { + "name" : "AIX", + "version" : "2" + } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; U; AIX 7.2; en-US; rv:1.7.12) Gecko/20100101 Firefox/126.0", + "expect" : + { + "name" : "AIX", + "version" : "7.2" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/fuchsia.json b/test/data/ua/os/fuchsia.json index 0759cba..1f2e050 100644 --- a/test/data/ua/os/fuchsia.json +++ b/test/data/ua/os/fuchsia.json @@ -7,5 +7,14 @@ "name" : "Fuchsia", "version" : "undefined" } + }, + { + "desc" : "Fuchsia", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Mozilla/5.0 (X11; Linux; Fuchsia; GoogleTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Large Screen Safari/537.36 GoogleTV", + "expect" : + { + "name" : "Fuchsia", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/haiku.json b/test/data/ua/os/haiku.json index 7005087..f253739 100644 --- a/test/data/ua/os/haiku.json +++ b/test/data/ua/os/haiku.json @@ -7,5 +7,32 @@ "name" : "Haiku", "version" : "R1" } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (X11; Haiku x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (Haiku; BeOS; rv:99.0) Gecko/20100101 Firefox/99.0", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (X11; Haiku BePC) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.17 Chrome/87.0.4280.144 Safari/537.36 Dooble/2023.12.25 Dooble/2023.12.25", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/solaris.json b/test/data/ua/os/solaris.json index 450e47d..4c8f673 100644 --- a/test/data/ua/os/solaris.json +++ b/test/data/ua/os/solaris.json @@ -5,7 +5,16 @@ "expect" : { "name" : "Solaris", - "version" : "sun4u" + "version" : "undefined" + } + }, + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (Solaris; SPARC; w3m/0.5.3; rv:1.0) Gecko/20100101 w3m/0.5.3", + "expect" : + { + "name" : "Solaris", + "version" : "undefined" } }, { @@ -16,5 +25,23 @@ "name" : "Solaris", "version" : "4.1.4" } + }, + { + "desc" : "Solaris", + "ua" : "Opera/5.0 (SunOS 5.8 sun4m; U) [en]", + "expect" : + { + "name" : "Solaris", + "version" : "5.8" + } + }, + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.8) Gecko/20100215 Solaris/10.1 (GNU) Superswan/3.5.8 (Byte/me)", + "expect" : + { + "name" : "Solaris", + "version" : "10.1" + } } ] \ No newline at end of file From 598c51c69cb6533ab3ab85085f32727ddb416fd3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 28 Mar 2025 05:23:36 +0700 Subject: [PATCH 03/30] Add new OS: ArcaOS https://www.arcanoae.com/arcaos/ --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/data/ua/os/arcaos.json | 20 ++++++++++++++++++++ test/data/ua/os/os2.json | 18 ++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/data/ua/os/arcaos.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 2fb0db5..8dff5ea 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -288,6 +288,7 @@ export const OS: Readonly<{ AMIGA_OS: "Amiga OS"; ANDROID: "Android"; ANDROID_X86: "Android-x86"; + ARCAOS: "ArcaOS"; ARCH: "Arch"; BADA: "Bada"; BEOS: "BeOS"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 0a53cce..4bfcec4 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -303,6 +303,7 @@ const OS = Object.freeze({ AMIGA_OS: 'Amiga OS', ANDROID: 'Android', ANDROID_X86: 'Android-x86', + ARCAOS: 'ArcaOS', ARCH: 'Arch', BADA: 'Bada', BEOS: 'BeOS', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d3bd002..b399874 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -977,7 +977,7 @@ // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux|fuchsia)[; ]/i, // Mageia/VectorLinux/Fuchsia + /(mageia|vectorlinux|fuchsia|arcaos)[; ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS /([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 /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris diff --git a/test/data/ua/os/arcaos.json b/test/data/ua/os/arcaos.json new file mode 100644 index 0000000..5097545 --- /dev/null +++ b/test/data/ua/os/arcaos.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "ArcaOS", + "ua" : "Mozilla/5.0 (OS/2; ArcaOS 5.0.6; x86_64; rv:89.0) Gecko/20100101 Firefox/89.0", + "expect" : + { + "name" : "ArcaOS", + "version" : "5.0.6" + } + }, + { + "desc" : "ArcaOS", + "ua" : "Mozilla/5.0 (OS/2; ArcaOS; x64; rv:89.0) Gecko/20100101 Firefox/89.0", + "expect" : + { + "name" : "ArcaOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/os2.json b/test/data/ua/os/os2.json index cb38c11..56eac7c 100644 --- a/test/data/ua/os/os2.json +++ b/test/data/ua/os/os2.json @@ -7,5 +7,23 @@ "name" : "OS/2", "version" : "undefined" } + }, + { + "desc" : "OS/2", + "ua" : "Mozilla/5.0 (OS/2; OS/2 i386) AppleWebKit/538.36 (KHTML, like Gecko) QtWebEngine/5.15.2 Chrome/127.0.6533.72", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } + }, + { + "desc" : "OS/2", + "ua" : "Mozilla/5.0 (OS/2; Warp 4.5; rv:45.0) Gecko/20100101 Firefox/45.0", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } } ] \ No newline at end of file From e1216ff36bc47f2f3664db831af4cf2e054d578a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 15 Apr 2025 21:32:53 +0700 Subject: [PATCH 04/30] Improve OS detection: Arch, Mint --- src/main/ua-parser.js | 8 +-- test/data/ua/os/arch.json | 18 +++++++ test/data/ua/os/debian.json | 9 ++++ test/data/ua/os/kubuntu.json | 9 ++++ test/data/ua/os/mint.json | 99 ++++++++++++++++++++++++++++++++++++ test/data/ua/os/suse.json | 9 ++++ test/data/ua/os/ubuntu.json | 27 ++++++++++ test/data/ua/os/zenwalk.json | 9 ++++ 8 files changed, 184 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b399874..f55d0e9 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -976,10 +976,10 @@ // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm - /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux|fuchsia|arcaos)[; ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS - /([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 + /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint + /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|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/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS diff --git a/test/data/ua/os/arch.json b/test/data/ua/os/arch.json index 0aad667..a186af0 100644 --- a/test/data/ua/os/arch.json +++ b/test/data/ua/os/arch.json @@ -1,4 +1,22 @@ [ + { + "desc" : "Arch", + "ua" : "Mozilla/5.0 (X11; Arch Linux i686; rv:2.0) Gecko/20100101 Firefox/126.1", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + }, + { + "desc" : "Arch", + "ua" : "Mozilla/5.0 ArchLinux (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + }, { "desc" : "Arch", "ua" : "Uzbl (Webkit 1.1.10) (Arch Linux)", diff --git a/test/data/ua/os/debian.json b/test/data/ua/os/debian.json index 4ae949a..a6a777b 100644 --- a/test/data/ua/os/debian.json +++ b/test/data/ua/os/debian.json @@ -52,5 +52,14 @@ "name" : "Debian", "version" : "undefined" } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (X11; U; Linux sparc64; es-PY; rv:5.0) Gecko/20100101 IceCat/5.0 (like Firefox/5.0; Debian-6.0.1)", + "expect" : + { + "name" : "Debian", + "version" : "6.0.1" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/kubuntu.json b/test/data/ua/os/kubuntu.json index 2634256..d242142 100644 --- a/test/data/ua/os/kubuntu.json +++ b/test/data/ua/os/kubuntu.json @@ -7,5 +7,14 @@ "name" : "Kubuntu", "version" : "undefined" } + }, + { + "desc" : "Kubuntu", + "ua" : "Mozilla/5.0 (Kubuntu; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0", + "expect" : + { + "name" : "Kubuntu", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/mint.json b/test/data/ua/os/mint.json index e87641e..8493d1a 100644 --- a/test/data/ua/os/mint.json +++ b/test/data/ua/os/mint.json @@ -25,5 +25,104 @@ "name" : "Mint", "version" : "6" } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4", + "expect" : + { + "name" : "Mint", + "version" : "6" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.80 (X11; Linux i686; Edition Linux Mint) Presto/2.12.388 Version/12.15", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.51 (X11; Linux i686; U; Linux Mint; en)", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/19.3 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Brave Chrome/83.0.4103.97 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "19.3" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/20 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Kiwi Chrome/100.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "20" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/20.2; x86_64) Gecko/20100101 Firefox/92.0.1", + "expect" : + { + "name" : "Mint", + "version" : "20.2" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.10) Gecko/2009042513 Linux Mint/5 (Elyssa) Firefox/3.0.10", + "expect" : + { + "name" : "Mint", + "version" : "5" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060308 Linux Mint/7 (Gloria) Firefox/54.0", + "expect" : + { + "name" : "Mint", + "version" : "7" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20091020 Linux Mint/8 (Helena) Firefox/3.5.3", + "expect" : + { + "name" : "Mint", + "version" : "8" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:2.0) Gecko/20100101 Linux Mint 16/Petra Firefox/25.0.1.", + "expect" : + { + "name" : "Mint", + "version" : "16" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (Linux x86_64; Mint 21.3) AppleWebKit/537.36 (KHTML, like Gecko) Brave/128.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "21.3" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/suse.json b/test/data/ua/os/suse.json index 9da3f35..57f5f69 100644 --- a/test/data/ua/os/suse.json +++ b/test/data/ua/os/suse.json @@ -7,5 +7,14 @@ "name" : "SUSE", "version" : "3.6.17-0.2.1" } + }, + { + "desc" : "SUSE", + "ua" : "Mozilla/5.0 (X11; SUSE; Linux amd64; rv:86.0) Gecko/20100101 Firefox/86.0", + "expect" : + { + "name" : "SUSE", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/ubuntu.json b/test/data/ua/os/ubuntu.json index d3958f9..5438e89 100644 --- a/test/data/ua/os/ubuntu.json +++ b/test/data/ua/os/ubuntu.json @@ -16,5 +16,32 @@ "name" : "Ubuntu", "version" : "undefined" } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (Wayland; Linux x86_64; Huawei) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Ubuntu/23.04 Edg/110.0.1587.41", + "expect" : + { + "name" : "Ubuntu", + "version" : "23.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (X11; Ubuntu 20.04; Linux arm; rv:99.0) Gecko/20100101 Firefox/99.0", + "expect" : + { + "name" : "Ubuntu", + "version" : "20.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16", + "expect" : + { + "name" : "Ubuntu", + "version" : "14.10" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/zenwalk.json b/test/data/ua/os/zenwalk.json index 230b5dd..170db50 100644 --- a/test/data/ua/os/zenwalk.json +++ b/test/data/ua/os/zenwalk.json @@ -7,5 +7,14 @@ "name" : "Zenwalk", "version" : "7.3" } + }, + { + "desc" : "Zenwalk", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.13) Gecko/20101221 IceCat/3.6.13 (like Firefox/3.6.13) (Zenwalk GNU Linux)", + "expect" : + { + "name" : "Zenwalk", + "version" : "undefined" + } } ] \ No newline at end of file From 848cd1d1e408e4f7f69ecbd89eae3b1e3b906405 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 15 Apr 2025 21:43:59 +0700 Subject: [PATCH 05/30] Add new OS: Knoppix, Xubuntu --- src/enums/ua-parser-enums.d.ts | 2 ++ src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 4 ++-- test/data/ua/os/knoppix.json | 11 +++++++++++ test/data/ua/os/xubuntu.json | 20 ++++++++++++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 test/data/ua/os/knoppix.json create mode 100644 test/data/ua/os/xubuntu.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 8dff5ea..dc9d4b8 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -319,6 +319,7 @@ export const OS: Readonly<{ IOS: "iOS"; JOLI: "Joli"; KAIOS: "KaiOS"; + KNOPPIX: "Knoppix"; KUBUNTU: "Kubuntu"; LINPUS: "Linpus"; LINSPIRE: "Linspire"; @@ -371,5 +372,6 @@ export const OS: Readonly<{ WINDOWS_MOBILE: "Windows Mobile"; WINDOWS_PHONE: "Windows Phone"; XBOX: "Xbox"; + XUBUNTU: "Xubuntu"; ZENWALK: "Zenwalk"; }>; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 4bfcec4..ee46d8b 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -334,6 +334,7 @@ const OS = Object.freeze({ IOS: 'iOS', JOLI: 'Joli', KAIOS: 'KaiOS', + KNOPPIX: 'Knoppix', KUBUNTU: 'Kubuntu', LINPUS: 'Linpus', LINSPIRE: 'Linspire', @@ -386,6 +387,7 @@ const OS = Object.freeze({ WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', XBOX: 'Xbox', + XUBUNTU: 'Xubuntu', ZENWALK: 'Zenwalk' // TODO : test! diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index f55d0e9..88864b7 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -978,8 +978,8 @@ /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch - /([kxln]?ubuntu|debian|suse|opensuse|gentoo|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/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire|knoppix)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire/Knoppix /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS diff --git a/test/data/ua/os/knoppix.json b/test/data/ua/os/knoppix.json new file mode 100644 index 0000000..4ca6cdb --- /dev/null +++ b/test/data/ua/os/knoppix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Knoppix", + "ua" : "Mozilla/5.0 (Knoppix; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.3", + "expect" : + { + "name" : "Knoppix", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/xubuntu.json b/test/data/ua/os/xubuntu.json new file mode 100644 index 0000000..1f38a96 --- /dev/null +++ b/test/data/ua/os/xubuntu.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Xubuntu", + "ua" : "Mozilla/5.0 (X11; Xubuntu Linux 21.10 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.81 Safari/537.57 Vivaldi/5.1.2 (Blink)", + "expect" : + { + "name" : "Xubuntu", + "version" : "21.10" + } + }, + { + "desc" : "Xubuntu", + "ua" : "Mozilla/5.0 (X11; Xubuntu 14.04.5 LTS) AppleWebKit/537.36 (KHTML, like Gecko) Xubuntu Chrome/66.0.2623.87 Safari/537.36", + "expect" : + { + "name" : "Xubuntu", + "version" : "14.04.5" + } + } +] \ No newline at end of file From e069133f516142ee274f92bb0edfa8c2e05fb64c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 15 Apr 2025 21:49:18 +0700 Subject: [PATCH 06/30] Fix #787 - Avoid using lookbehind assertion in regex to ensure compatibility --- src/extensions/ua-parser-extensions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 6665b6d..2929f3a 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -329,7 +329,8 @@ const Libraries = Object.freeze({ // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent [ /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, - /(jsdom|(?<=\()java)\/([\w\.]+)/i + /(jsdom)\/([\w\.]+)/i, + /\((java)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, LIBRARY]] ] }); From b9a710978e88ff1d5480886c2552efaccdad78ae Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 2 May 2025 10:19:19 +0700 Subject: [PATCH 07/30] Fix #791 - Add new device vendor: Vizio --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 1 + test/data/ua/device/vizio.json | 20 ++++++++++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 test/data/ua/device/vizio.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index dc9d4b8..e5bec20 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -255,6 +255,7 @@ export const Vendor: Readonly<{ TESLA: "Tesla"; ULEFONE: "Ulefone"; VIVO: "Vivo"; + VIZIO: "Vizio"; VODAFONE: "Vodafone"; XBOX: "Xbox"; XIAOMI: "Xiaomi"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ee46d8b..84615b6 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -266,6 +266,7 @@ const Vendor = Object.freeze({ TESLA: 'Tesla', ULEFONE: 'Ulefone', VIVO: 'Vivo', + VIZIO: 'Vizio', VODAFONE: 'Vodafone', XBOX: 'Xbox', XIAOMI: 'Xiaomi', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 88864b7..51c8812 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -766,6 +766,7 @@ ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(vizio)(?: |.+model\/)(\w+-\w+)/i, // Vizio /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i diff --git a/test/data/ua/device/vizio.json b/test/data/ua/device/vizio.json new file mode 100644 index 0000000..d36ae4d --- /dev/null +++ b/test/data/ua/device/vizio.json @@ -0,0 +1,20 @@ +[ + { + "desc": "VIZIO", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36 CrKey/1.0.999999 VIZIO SmartCast(Conjure/SX7B-4.6.419.12 FW/7.0.23.2-4 Model/M557-G0)", + "expect": { + "vendor": "VIZIO", + "model": "M557-G0", + "type": "smarttv" + } + }, + { + "desc": "VIZIO", + "ua": "VIZIO V506-J09 ViziOS/1.4.512.847.1 WatchFree/24.06.13.2 FancyPlayer/1.1.30-qa ", + "expect": { + "vendor": "VIZIO", + "model": "V506-J09", + "type": "smarttv" + } + } +] \ No newline at end of file From 944c1fb26017273b66d65e80e994394b3b0186a5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 10 May 2025 12:28:57 +0700 Subject: [PATCH 08/30] Improve device detection: Motorola --- src/main/ua-parser.js | 17 +- test/data/ua/device/motorola.json | 269 ++++++++++++++++++++++++++---- 2 files changed, 247 insertions(+), 39 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 51c8812..9152236 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -597,10 +597,17 @@ /\b(rmx[1-3]\d{3})(?: bui|;|\))/i ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ + // Lenovo + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ + /lenovo[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, MOBILE]], [ + // Motorola /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, - /\bmot(?:orola)?[- ](\w*)/i, - /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i + /\bmot(?:orola)?[- ]([\w\s]+)(\)| bui)/i, + /((?:moto(?! 360)[-\w\(\) ]+|xt\d{3,4}[cgkosw\+]?[-\d]*|nexus 6)(?= bui|\)))/i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ /\b(mz60\d|xoom[2 ]{0,2}) build\//i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ @@ -613,11 +620,6 @@ /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ - // Lenovo - /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, - /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i - ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ - // Nokia /(nokia) (t[12][01])/i ], [VENDOR, MODEL, [TYPE, TABLET]], [ @@ -734,7 +736,6 @@ /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ diff --git a/test/data/ua/device/motorola.json b/test/data/ua/device/motorola.json index b9227ba..18d1e48 100644 --- a/test/data/ua/device/motorola.json +++ b/test/data/ua/device/motorola.json @@ -1,28 +1,10 @@ [ { - "desc": "Motorola Moto X", - "ua": "Mozilla/5.0 (Linux; Android 4.4.4; XT1097 Build/KXE21.187-38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "desc": "Motorola Defy XT", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; XT555C Build/V1.67D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", "expect": { "vendor": "Motorola", - "model": "XT1097", - "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": "Motorola Nexus 6", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "Nexus 6", + "model": "XT555C", "type": "mobile" } }, @@ -35,6 +17,42 @@ "type": "mobile" } }, + { + "desc": "Motorola Edge 30 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; XT2201-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2201-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge 30 Ultra", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola edge 30 ultra Build/U1SQS34.52-21-1-13; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 M", + "expect": { + "vendor": "Motorola", + "model": "edge 30 ultra", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge 40", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola edge 40 Build/U1TL34.115-16-1-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.104 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "edge 40", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge X30", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2201-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.59 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2201-2", + "type": "mobile" + } + }, { "desc": "Motorola RAZR 2019", "ua": "Mozilla/5.0 (Linux; Android 9; motorola razr) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.1 Chrome/75.0.3770.143 Mobile Safari/537.36", @@ -45,11 +63,65 @@ } }, { - "desc": "Moto X", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "desc": "Motorola Moto 360", + "ua": "Mozilla/5.0 (Linux; Android 4.4; Moto 360 Build/KNX01S) AppleWebKit/537.36 (KHTML, like Gecko) WIB/0.9.8 Mobile Safari/537.36", "expect": { "vendor": "Motorola", - "model": "XT1058", + "model": "Moto 360", + "type": "wearable" + } + }, + { + "desc": "Motorola Moto E", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NDQS26.69-64-11-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto E (4)", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E6s", + "ua": "Mozilla/5.0 (Linux; Android 9; MOTOROLA E6S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "E6S", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E7 Plus", + "ua": "Mozilla/5.0 (Linux; Android 10; moto e(7) plus Build/QPZS30.30-Q3-38-69-12; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.14 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "moto e(7) plus", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E7 Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2081-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Mobile Safari/537.36 OPR/102.0.4864.0", + "expect": { + "vendor": "Motorola", + "model": "XT2081-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; moto g 5G - 2023 Build/U1TPNS34.26-48-2-7; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Motorola", + "model": "moto g 5G - 2023", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G 5G (2022)", + "ua": "Mozilla/5.0 (Linux; Android 12; moto g 5G (2022) Build/S1SAS32.47-77-9; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36 [FB_IAB/Orca-Android;FBAV/402.0.0.11.101;]", + "expect": { + "vendor": "Motorola", + "model": "moto g 5G (2022)", "type": "mobile" } }, @@ -72,21 +144,156 @@ } }, { - "desc": "Motorola Moto E", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NDQS26.69-64-11-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "desc": "Motorola Moto G10", + "ua": "Mozilla/5.0 (Linux; Android 12; XT2127-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36", "expect": { "vendor": "Motorola", - "model": "Moto E (4)", + "model": "XT2127-2", "type": "mobile" } }, { - "desc": "Motorola Moto 360", - "ua": "Mozilla/5.0 (Linux; Android 4.4; Moto 360 Build/KNX01S) AppleWebKit/537.36 (KHTML, like Gecko) WIB/0.9.8 Mobile Safari/537.36", + "desc": "Motorola Moto G24 Power", + "ua": "Mozilla/5.0 (Linux; U; Android 14; moto g24 power Build/UTAS34.82-97-1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.138 Mobile Safari/537.36 OPR/89.0.2254.76420", "expect": { "vendor": "Motorola", - "model": "Moto 360", - "type": "wearable" + "model": "moto g24 power", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G30", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2129-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2129-3", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G54 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; moto g54 5G Build/U1TDS34.94-12-9-10-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.122 Mobile Safari/537.36 OPX/2", + "expect": { + "vendor": "Motorola", + "model": "moto g54 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; XT1097 Build/KXE21.187-38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT1097", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Motorola", + "model": "XT1058", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X Force", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; XT1580 Build/NPKS25.200-12-9; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36 OPR/32.0.2254.123747", + "expect": { + "vendor": "Motorola", + "model": "XT1580", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto S30", + "ua": "Mozilla/5.0 (Linux; Android 13; XT2243-2 Build/T1SJC33.51-19-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36 T7/13.30 SP-engine/2.53.0 matrixstyle/0 lite baiduboxapp/6.2.0.10 (Baidu; P1 13) NABar/1.0", + "expect": { + "vendor": "Motorola", + "model": "XT2243-2", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X30 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; XT2241-1 Build/T1SQ33.111-12-19) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.1.3.1264 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2241-1", + "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": "Motorola Moto Z4", + "ua": "Mozilla/5.0 (Linux; U; Android 10; moto z4 Build/QDF30.130-42-5-17; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 OPR/2.3.0", + "expect": { + "vendor": "Motorola", + "model": "moto z4", + "type": "mobile" + } + }, + { + "desc": "Motorola Nexus 6", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Nexus 6", + "type": "mobile" + } + }, + { + "desc": "Motorola One 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; motorola one 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "one 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola P30", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; XT1943-1 Build/OPM1.171019.019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36 T7/13.50 SP-engine/2.88.0 baiduboxapp/13.50.5.10 (Baidu; P1 8.1.0) NABar/1.0", + "expect": { + "vendor": "Motorola", + "model": "XT1943-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; motorola razr 5G Build/S2PS32.57-23-21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;IABMV/1;]", + "expect": { + "vendor": "Motorola", + "model": "razr 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; XT2071-4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2071-4", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 50 Ultra", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola razr 50 ultra Build/U3UX34.56-29-2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.260 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "razr 50 ultra", + "type": "mobile" } } ] \ No newline at end of file From 14a51039aa4fb67fbfadf4eb44a10b84e9c0c77d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 10 May 2025 19:12:26 +0700 Subject: [PATCH 09/30] Improve device detection: identify `Large Screen` as `smarttv` --- src/main/ua-parser.js | 2 +- test/data/ua/device/_others.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 9152236..a5ab086 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -803,7 +803,7 @@ // SmartTV from Unidentified Vendors /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i ], [MODEL, [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:|large screen[\w ]+safari)\b/i ], [[TYPE, SMARTTV]], [ /////////////////// diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index c47da24..b545f39 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -281,6 +281,24 @@ "type": "smarttv" } }, + { + "desc": "Unknown TV", + "ua": "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.127 Large Screen Safari/533.4 GoogleTV/ 162671", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Unknown TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen WebAppManager Safari/537.41", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, { "desc": "PDA with Windows CE", "ua": "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", From 6a2a3a61b63c52887f18655ec4f3cf99cdcdfe0c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 11:37:44 +0700 Subject: [PATCH 10/30] Fix mistakenly placed enum value --- src/enums/ua-parser-enums.d.ts | 2 +- src/enums/ua-parser-enums.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index e5bec20..7552764 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -16,7 +16,6 @@ export const Browser: Readonly<{ BAIDU: "Baidu Browser"; BASILISK: "Basilisk"; BLAZER: "Blazer"; - BLU: "BLU"; BOLT: "Bolt"; BOWSER: "Bowser"; BRAVE: "Brave"; @@ -203,6 +202,7 @@ export const Vendor: Readonly<{ ATT: "AT&T"; BENQ: "BenQ"; BLACKBERRY: "BlackBerry"; + BLU: "BLU"; CAT: "Cat"; DELL: "Dell"; ENERGIZER: "Energizer"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 84615b6..4a784b4 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -21,7 +21,6 @@ const Browser = Object.freeze({ BAIDU: 'Baidu Browser', BASILISK: 'Basilisk', BLAZER: 'Blazer', - BLU: 'BLU', BOLT: 'Bolt', BOWSER: 'Bowser', BRAVE: 'Brave', @@ -214,6 +213,7 @@ const Vendor = Object.freeze({ ATT: 'AT&T', BENQ: 'BenQ', BLACKBERRY: 'BlackBerry', + BLU: 'BLU', CAT: 'Cat', DELL: 'Dell', ENERGIZER: 'Energizer', From be16c04a33c8c0833b08e43a3c784e4701f2dc66 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 14:00:58 +0700 Subject: [PATCH 11/30] Improve device detection: Nothing, Sony --- src/main/ua-parser.js | 4 +-- test/data/ua/device/nothing.json | 36 +++++++++++++++++++++++++ test/data/ua/device/sony.json | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a5ab086..09f2806 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -634,7 +634,7 @@ ], [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 + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ /sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i @@ -715,7 +715,7 @@ ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ // Nothing - /droid.+; (a(?:015|06[35]|142p?))/i + /droid.+; (a(in)?(0(15|59|6[35])|142)p?)/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // Archos diff --git a/test/data/ua/device/nothing.json b/test/data/ua/device/nothing.json index 56dc460..12b418d 100644 --- a/test/data/ua/device/nothing.json +++ b/test/data/ua/device/nothing.json @@ -17,6 +17,15 @@ "type": "mobile" } }, + { + "desc": "Nothing 2", + "ua": "Mozilla/5.0 (Linux; Android 15; AIN065 Build/AQ3A.240929.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.90 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/496.0.0.45.65;IABMV/1;] FBNV/500", + "expect": { + "vendor": "Nothing", + "model": "AIN065", + "type": "mobile" + } + }, { "desc": "Nothing 2a", "ua": "Mozilla/5.0 (Linux; Android 14; A142 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", @@ -25,5 +34,32 @@ "model": "A142", "type": "mobile" } + }, + { + "desc": "Nothing 2a Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; A142P Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.107 Mobile Safari/537.36 OPX/2.6", + "expect": { + "vendor": "Nothing", + "model": "A142P", + "type": "mobile" + } + }, + { + "desc": "Nothing 3a", + "ua": "Mozilla/5.0 (Linux; Android 15; A059 Build/AQ3A.241015.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A059", + "type": "mobile" + } + }, + { + "desc": "Nothing 3a Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; A059P Build/AQ3A.241015.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/136.0.7103.57 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/511.0.0.73.36;IABMV/1;]", + "expect": { + "vendor": "Nothing", + "model": "A059P", + "type": "mobile" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/sony.json b/test/data/ua/device/sony.json index 92151af..d4a3c21 100644 --- a/test/data/ua/device/sony.json +++ b/test/data/ua/device/sony.json @@ -98,6 +98,15 @@ "type": "mobile" } }, + { + "desc": "Sony Xperia 1 VI", + "ua": "Mozilla/5.0 (Linux; Android 14; XQ-EC44) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-EC44", + "type": "mobile" + } + }, { "desc": "Sony Xperia 10ii", "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AU52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", @@ -107,6 +116,24 @@ "type": "mobile" } }, + { + "desc": "Sony Xperia 10 III Lite", + "ua": "Mozilla/5.0 (Linux; Android 12; XQ-BT44 Build/62.1.A.1.227; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 Sleipnir/3.6.0", + "expect": { + "vendor": "Sony", + "model": "XQ-BT44", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 10 VI", + "ua": "Mozilla/5.0 (Linux; Android 14; XQ-ES72 Build/70.0.A.2.233; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.71 Mobile Safari/537.36 Line/14.16.0/IAB", + "expect": { + "vendor": "Sony", + "model": "XQ-ES72", + "type": "mobile" + } + }, { "desc": "Sony Xperia Pro", "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", @@ -116,6 +143,24 @@ "type": "mobile" } }, + { + "desc": "Sony Xperia Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; Sony XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AQ52", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia Pro I", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; XQ-BE72) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.148 YaBrowser/22.7.3.82.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-BE72", + "type": "mobile" + } + }, { "desc": "Sony SGP521 (Xperia Z2 Tablet)", "ua": "Mozilla/5.0 (Linux; Android 4.4; SGP521 Build/17.1.A.0.432) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", From 3d6e326f49602bfb39bbb034e87f7dbb53e3b38a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 15:46:37 +0700 Subject: [PATCH 12/30] Improve device detection: Google Pixel & Pixelbook series --- src/main/ua-parser.js | 5 +- test/data/ua/device/google.json | 468 +++++++++++++++++++++++++++++--- 2 files changed, 427 insertions(+), 46 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 09f2806..d6b7e04 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -630,8 +630,11 @@ // Google /(pixel (c|tablet))\b/i // Google Pixel C/Tablet ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ - /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel + // Google Pixel + /droid.+;(?: google)? (g(01[13]a|020[aem]|025[jn]|1b60|1f8f|2ybb|4s1m|576d|5nz6|8hhn|8vou|a02099|c15s|d1yq|e2ae|ec77|gh2x|kv4x|p4bc|pj41|r83y|tt9q|ur25|wvk6)|pixel[\d ]*a?( pro)?( xl)?( fold)?( \(5g\))?)( bui|\))/i ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ + /(google) (pixelbook( go)?)/i + ], [VENDOR, MODEL], [ // Sony /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i diff --git a/test/data/ua/device/google.json b/test/data/ua/device/google.json index a06b991..0897534 100644 --- a/test/data/ua/device/google.json +++ b/test/data/ua/device/google.json @@ -71,51 +71,6 @@ "type": "mobile" } }, - { - "desc": "Google Pixel Tablet", - "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel Tablet", - "type": "tablet" - } - }, - { - "desc": "Google Pixel Watch", - "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", - "expect": { - "vendor": "Google", - "model": "Pixel Watch", - "type": "wearable" - } - }, - { - "desc": "Google Pixel Watch 2", - "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", - "expect": { - "vendor": "Google", - "model": "Pixel Watch 2", - "type": "wearable" - } - }, - { - "desc": "Google Pixel XL", - "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel XL", - "ua": "Mozilla/5.0 (Linux; Android 9; Pixel XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel XL", - "type": "mobile" - } - }, { "desc": "Google Pixel 2", "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", @@ -125,6 +80,24 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 2) AppleWebKit/537.36 (KHTML, like Gecko) Edg/57.0.986.6", + "expect": { + "vendor": "Google", + "model": "Pixel 2", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; G011A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Safari/537.36 OPR/68.3.3557.64528", + "expect": { + "vendor": "Google", + "model": "G011A", + "type": "mobile" + } + }, { "desc": "Google Pixel 2 XL", "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 XL Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", @@ -152,6 +125,24 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 3", + "ua": "Mozilla/5.0 (Linux; Android 11; G013A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G013A", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3a", + "ua": "Mozilla/5.0 (Linux; Android 14; G020E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G020E", + "type": "mobile" + } + }, { "desc": "Google Pixel 3 XL", "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", @@ -197,6 +188,15 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 4", + "ua": "Mozilla/5.0 (Linux; Android 12; G020M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G020M", + "type": "mobile" + } + }, { "desc": "Google Pixel 4a", "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", @@ -206,6 +206,42 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 13; G025J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G025J", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 12; GA02099) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GA02099", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 13; G025N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G025N", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a (5G)", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 4a (5G) Build/UP1A.231105.001.B2; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Google", + "model": "Pixel 4a (5G)", + "type": "mobile" + } + }, { "desc": "Google Pixel 4 XL", "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", @@ -215,6 +251,33 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 13; G5NZ6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G5NZ6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 12; GD1YQ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GD1YQ", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 12; GTT9Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GTT9Q", + "type": "mobile" + } + }, { "desc": "Google Pixel 5", "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.120 Mobile Safari/537.36", @@ -224,6 +287,51 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 5a 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; G4S1M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G4S1M", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5a 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; G1F8F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G1F8F", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6", + "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 6 Build/QP1A.190711.020) AppleWebKit/545.31 (KHTML, like Gecko) Firefox/109.0.2318.118 Mobile Safari/545.22", + "expect": { + "vendor": "Google", + "model": "Pixel 6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel 6 Pro Build/AP4A.241205.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36 Line/15.0.0/IAB", + "expect": { + "vendor": "Google", + "model": "Pixel 6 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; G8VOU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G8VOU", + "type": "mobile" + } + }, { "desc": "Google Pixel 7", "ua": "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", @@ -232,5 +340,275 @@ "model": "Pixel 7", "type": "mobile" } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel 7 Pro Build/AP4A.250205.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.121 Mobile Safari/537.36 musical_ly_2023808030 BytedanceWebview/d8a21c6", + "expect": { + "vendor": "Google", + "model": "Pixel 7 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; GP4BC) AppleWebKit/537.46 (KHTML, like Gecko) Chrome/118.0.5993.80 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GP4BC", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; GE2AE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.48 Mobile Safari/537.46", + "expect": { + "vendor": "Google", + "model": "GE2AE", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7a", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 7a Build/AP2A.240905.003; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Google", + "model": "Pixel 7a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Build/UPB2.230407.014) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.2.7822.95 Mobile Safari/537.36 Vivaldi/5.2.9076.131", + "expect": { + "vendor": "Google", + "model": "Pixel 8", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro Build/UPB1.230309.017; Rooted) AppleWebKit/537.36 (KHTML, like Gecko) Puffin/10.9.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 8 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 14; Google Pixel 8a) Chrome/121.0.6167 Mobile", + "expect": { + "vendor": "Google", + "model": "Pixel 8a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; GKV4X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GKV4X", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; G8HHN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G8HHN", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; G576D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G576D", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; U; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.119 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 9", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; G2YBB) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G2YBB", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; GUR25) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GUR25", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; GWVK6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GWVK6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; G1B60) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G1B60", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro Build/AD1A.240530.047; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.170 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/506.1.0.74.27;IABMV/1;]", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; GR83Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GR83Y", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; GEC77) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GEC77", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro Fold", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; GC15S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GC15S", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; GGH2X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GGH2X", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro XL", + "ua": "Mozilla/5.0 (Linux; U; Android 15; Pixel 9 Pro XL Build/AP4A.250105.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.89 Mobile Safari/537.36 OPR/88.0.2254.75874", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel Fold Build/AP4A.250105.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.40.36;IABMV/1;]", + "expect": { + "vendor": "Google", + "model": "Pixel Fold", + "type": "mobile" + } + }, + { + "desc": "Google Pixel Tablet", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel Tablet", + "type": "tablet" + } + }, + { + "desc": "Google Pixel Watch", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch", + "type": "wearable" + } + }, + { + "desc": "Google Pixel Watch 2", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch 2", + "type": "wearable" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixelbook", + "ua": "Mozilla/5.0 (Linux; Android 9; Google Pixelbook) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixelbook", + "type": "undefined" + } + }, + { + "desc": "Google Pixelbook Go", + "ua": "Mozilla/5.0 (Linux; Android 9; Google Pixelbook Go) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 OPR/62.3.3146.57763", + "expect": { + "vendor": "Google", + "model": "Pixelbook Go", + "type": "undefined" + } } ] \ No newline at end of file From 466a8b26be6e931aafd4eb981607ce9b28fca5f4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 21:10:55 +0700 Subject: [PATCH 13/30] Improve device detection: OnePlus --- src/main/ua-parser.js | 14 +- test/data/ua/device/_others.json | 9 -- test/data/ua/device/oneplus.json | 228 ++++++++++++++++++++++++++++++- test/data/ua/device/oppo.json | 9 -- test/unit/ua-ch.js | 2 +- 5 files changed, 230 insertions(+), 32 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d6b7e04..d1624da 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -578,12 +578,17 @@ / ([\w ]+) miui\/v?\d/i ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + // OnePlus + /droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i, + /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ + // 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]], [ /\b(opd2(\d{3}a?))(?: bui|\))/i - ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ // BLU Vivo Series /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i @@ -643,11 +648,6 @@ /\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|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show @@ -736,7 +736,7 @@ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL + /; (blu|hmd|imo|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(oppo) ?([\w ]+) bui/i // OPPO diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index b545f39..c58a01e 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -122,15 +122,6 @@ "type": "undefined" } }, - { - "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": "Philips SmartTV", "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", diff --git a/test/data/ua/device/oneplus.json b/test/data/ua/device/oneplus.json index 74ddcf6..a02c0f3 100644 --- a/test/data/ua/device/oneplus.json +++ b/test/data/ua/device/oneplus.json @@ -54,11 +54,20 @@ } }, { - "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", + "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": "OnePlus", - "model": "KB2005", + "model": "HD1913", + "type": "mobile" + } + }, + { + "desc": "OnePlus 8 Lite", + "ua": "Mozilla/5.0 (Linux; Android 10; OnePlus 8 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4741.47 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "8 Lite", "type": "mobile" } }, @@ -72,11 +81,191 @@ } }, { - "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", + "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", "expect": { "vendor": "OnePlus", - "model": "BE2015", + "model": "KB2005", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9", + "ua": "Mozilla/5.0 (Android 14; SDK 28; OnePlus 9) Gecko/128.0 Firefox/128.0", + "expect": { + "vendor": "OnePlus", + "model": "9", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9", + "ua": "Mozilla/5.0 (Linux; Android 13; LE2113 Build/TP1A.220905.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 Flipboard/4.3.14/5377,4.3.14.5377", + "expect": { + "vendor": "OnePlus", + "model": "LE2113", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; LE2123) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Mobile Safari/537.36 OPR/73.0.3832.69371", + "expect": { + "vendor": "OnePlus", + "model": "LE2123", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9R", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; LE2100) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 YaApp_Android/21.114.1 YaSearchBrowser/21.114.1 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "LE2100", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9RT 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 11; en-in; MT2111 Build/RKQ1.210614.002) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 GSA/13.14.15.23.arm64", + "expect": { + "vendor": "OnePlus", + "model": "MT2111", + "type": "mobile" + } + }, + { + "desc": "OnePlus 10 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; NE2213) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.148 YaBrowser/22.7.5.90.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "NE2213", + "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": "OnePlus", + "model": "CPH2413", + "type": "mobile" + } + }, + { + "desc": "OnePlus 11R", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2487) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2487", + "type": "mobile" + } + }, + { + "desc": "OnePlus 13", + "ua": "Mozilla/5.0 (Linux; Android 15; CPH2653 Build/AP3A.240617.008; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.92 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/504.0.0.69.64;IABMV/1;] FBNV/5", + "expect": { + "vendor": "OnePlus", + "model": "CPH2653", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PGKM10 Build/AP3A.240617.008) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.168 Mobile Safari/537.36 HeyTapBrowser/40.9.6.2", + "expect": { + "vendor": "OnePlus", + "model": "PGKM10", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-CN; PHK110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.3.6.1367 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PHK110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2 Pro", + "ua": "Mozilla/5.0 (Linux; arm; Android 13; PJA110) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 YaBrowser/23.1.0.284.00 (beta) SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJA110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2V", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PHP110 Build/AP3A.240617.008) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/121.0.6167.71 MQQBrowser/16.3 Mobile Safari/537.36 COVC/047707", + "expect": { + "vendor": "OnePlus", + "model": "PHP110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-CN; PJE110 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.80 Quark/7.9.7.782 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJE110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3 Pro 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-CN; PJX110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.80 Quark/7.11.4.814 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJX110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3V", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-cn; PJF110 Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.61 Mobile Safari/537.36 HeyTapBrowser/40.8.33.1.2beta", + "expect": { + "vendor": "OnePlus", + "model": "PJF110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 5", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PKG110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.168 Mobile Safari/537.36 HeyTapBrowser/40.9.9.2", + "expect": { + "vendor": "OnePlus", + "model": "PKG110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-CN; PGP110 Build/UKQ1.230924.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.2.9.1360 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PGP110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace Racing Edition", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; PGZ110 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.3.6.1367 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PGZ110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord CE 4", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2613) AppleWebKit/537.36 (KHTML, like Gecko) JioSphere/5.0.4 Chrome/119.0.6045.193 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2613", "type": "mobile" } }, @@ -89,6 +278,33 @@ "type": "mobile" } }, + { + "desc": "OnePlus Nord N30 SE 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2605 Build/TP1A.220905.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/135.0.7049.92 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/509.0.0.67.28;IABMV/1;]", + "expect": { + "vendor": "OnePlus", + "model": "CPH2605", + "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", + "expect": { + "vendor": "OnePlus", + "model": "BE2015", + "type": "mobile" + } + }, + { + "desc": "OnePlus Open", + "ua": "Dalvik/2.1.0 (Linux; U; Android 15; CPH2551 Build/AP3A.240617.008)", + "expect": { + "vendor": "OnePlus", + "model": "CPH2551", + "type": "mobile" + } + }, { "desc": "OnePlus Pad Go 11.35", "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; OPD2304) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.629 YaApp_Android/24.101/apad YaSearchBrowser/24.101/apad BroPP/1.0 SA/3 Mobile Safari/537.36", diff --git a/test/data/ua/device/oppo.json b/test/data/ua/device/oppo.json index 367039e..ab63e26 100644 --- a/test/data/ua/device/oppo.json +++ b/test/data/ua/device/oppo.json @@ -1,13 +1,4 @@ [ - { - "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": "OPPO Pad", "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 54e2f9a..18dd09a 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -470,7 +470,7 @@ describe('Identify vendor & type of device from given model name', () => { { model: 'CPH2389', expect: { - vendor : 'OPPO', + vendor : 'OnePlus', type : 'mobile' } }, From 72c59b5351ed595eb0ffed4fb2911400078851c9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 13 May 2025 11:18:41 +0700 Subject: [PATCH 14/30] Add new device vendor: Lava https://www.lavamobiles.com/ --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/data/ua/device/lava.json | 110 +++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 test/data/ua/device/lava.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 7552764..b41dfc4 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -221,6 +221,7 @@ export const Vendor: Readonly<{ ITEL: "itel"; JOLLA: "Jolla"; KOBO: "Kobo"; + LAVA: "Lava"; LENOVO: "Lenovo"; LG: "LG"; MEIZU: "Meizu"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 4a784b4..2ae437c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -232,6 +232,7 @@ const Vendor = Object.freeze({ ITEL: 'itel', JOLLA: 'Jolla', KOBO: 'Kobo', + LAVA: 'Lava', LENOVO: 'Lenovo', LG: 'LG', MEIZU: 'Meizu', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d1624da..9d9279d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -736,7 +736,7 @@ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/OnePlus/TCL + /; (blu|hmd|imo|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Lava/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(oppo) ?([\w ]+) bui/i // OPPO diff --git a/test/data/ua/device/lava.json b/test/data/ua/device/lava.json new file mode 100644 index 0000000..5279f2c --- /dev/null +++ b/test/data/ua/device/lava.json @@ -0,0 +1,110 @@ +[ + { + "desc": "Lava Agni 2 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; LAVA LXX504 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.46 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/499.0.0.31.60;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LXX504", + "type": "mobile" + } + }, + { + "desc": "Lava Agni 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; LAVA LXX501 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.119 Mobile Safari/537.36[FBAN/EMA;FBLC/hi_IN;FBAV/396.0.0.9.115;]", + "expect": { + "vendor": "LAVA", + "model": "LXX501", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze", + "ua": "Mozilla/5.0 (Linux; U; Android 12; en-US; LAVA Blaze Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.4.0.1306 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "Blaze", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze 2", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LZX409 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LZX409", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; LAVA LXX503) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LXX503", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze Curve", + "ua": "Mozilla/5.0 (Linux; Android 14; LAVA LXX505 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/505.0.0.57.45;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LXX505", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LZX404 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.50 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/503.0.0.69.76;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LZX404", + "type": "mobile" + } + }, + { + "desc": "Lava Iris 46", + "ua": "Mozilla/5.0 (Linux; Android 9; LAVA LH9950) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/21.0 Chrome/110.0.5481.154 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LH9950", + "type": "mobile" + } + }, + { + "desc": "Lava Iris 54", + "ua": "Mozilla/5.0 (Linux; U; Android 9; LAVA LH9931 Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 OPR/39.1.2254.136708", + "expect": { + "vendor": "LAVA", + "model": "LH9931", + "type": "mobile" + } + }, + { + "desc": "Lava Storm 5G", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LXX508 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.100 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LXX508", + "type": "mobile" + } + }, + { + "desc": "Lava V7 Prime", + "ua": "Mozilla/5.0 (Linux; Android 9; LAVA LE9940) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LE9940", + "type": "mobile" + } + }, + { + "desc": "Lava V7s Prime", + "ua": "Mozilla/5.0 (Linux; U; Android 9; LAVA LE9940_W Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 OPR/43.2.2254.140294", + "expect": { + "vendor": "LAVA", + "model": "LE9940_W", + "type": "mobile" + } + } +] \ No newline at end of file From f51ed7911856fe87786503a22b4cec42fbd7f5f8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 20 May 2025 16:41:00 +0700 Subject: [PATCH 15/30] Improve device detection: HMD, Infinix, Tecno --- src/main/ua-parser.js | 12 ++++++++---- test/data/ua/device/hmd.json | 27 +++++++++++++++++++++++++++ test/data/ua/device/infinix.json | 27 +++++++++++++++++++++++++++ test/data/ua/device/tecno.json | 27 +++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 9d9279d..ae91dbd 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -729,14 +729,18 @@ /; (ac[3-6]\d\w{2,8})( b|\))/i ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + // HMD + /; (n159v)/i + ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [ + // MIXED /(imo) (tab \w+)/i, // IMO - /(infinix) (x1101b?)/i // Infinix XPad + /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Lava/OnePlus/TCL + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan + /; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(oppo) ?([\w ]+) bui/i // OPPO diff --git a/test/data/ua/device/hmd.json b/test/data/ua/device/hmd.json index a7d38e2..037148c 100644 --- a/test/data/ua/device/hmd.json +++ b/test/data/ua/device/hmd.json @@ -8,6 +8,15 @@ "type": "mobile" } }, + { + "desc": "HMD Fusion", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Fusion) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Fusion", + "type": "mobile" + } + }, { "desc": "HMD Pulse", "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", @@ -34,5 +43,23 @@ "model": "Pulse Pro", "type": "mobile" } + }, + { + "desc": "HMD Skyline", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; HMD Skyline) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.1804 YaApp_Android/24.120.1 YaSearchBrowser/24.120.1 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Skyline", + "type": "mobile" + } + }, + { + "desc": "HMD Vibe", + "ua": "Mozilla/5.0 (Linux; Android 14; N159V Build/UKQ1.231025.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/135.0.7049.111 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;]", + "expect": { + "vendor": "HMD", + "model": "N159V", + "type": "mobile" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/infinix.json b/test/data/ua/device/infinix.json index 6c356d3..3474003 100644 --- a/test/data/ua/device/infinix.json +++ b/test/data/ua/device/infinix.json @@ -1,4 +1,22 @@ [ + { + "desc": "Infinix Hot 4", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; en-us; Infinix HOT 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36 PHX/17.9", + "expect": { + "vendor": "Infinix", + "model": "HOT 4", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 4 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Infinix_X556_LTE Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/390.0.0.27.105;]", + "expect": { + "vendor": "Infinix", + "model": "X556_LTE", + "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", @@ -26,6 +44,15 @@ "type": "mobile" } }, + { + "desc": "Infinix Note 3", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Infinix_X601_LTE Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/81.0.4044.111 Mobile Safari/537.36 GSA/11.5.9.21.arm64", + "expect": { + "vendor": "Infinix", + "model": "X601_LTE", + "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", diff --git a/test/data/ua/device/tecno.json b/test/data/ua/device/tecno.json index a002fcf..4413f95 100644 --- a/test/data/ua/device/tecno.json +++ b/test/data/ua/device/tecno.json @@ -1,4 +1,31 @@ [ + { + "desc": "Tecno DroiPad 8D", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; TECNO DP8D Build/LMY47D; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36 OPR/42.0.2254.139280", + "expect": { + "vendor": "TECNO", + "model": "DP8D", + "type": "tablet" + } + }, + { + "desc": "Tecno DroiPad 10A Pro", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TECNO DP10A Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "DP10A Pro", + "type": "tablet" + } + }, + { + "desc": "Tecno DroiPad 10D 4G", + "ua": "Mozilla/5.0 (Linux; Android 7.0; TECNO P904) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36 OPR/77.0.4054.90", + "expect": { + "vendor": "TECNO", + "model": "P904", + "type": "tablet" + } + }, { "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", From 64a933bae8cc5f9bc2638300ed608faab79fd7bd Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 20 May 2025 16:45:47 +0700 Subject: [PATCH 16/30] [extensions] Add new fetcher: `MistralAI-User`, `Perplexity-User` --- src/extensions/ua-parser-extensions.js | 4 +++- test/data/ua/extension/fetcher.json | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 2929f3a..8317f4b 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -222,8 +222,10 @@ const Fetchers = Object.freeze({ // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan + // Perplexity-User - https://docs.perplexity.ai/guides/bots + // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index fed25bc..62154a9 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -149,6 +149,26 @@ "type" : "fetcher" } }, + { + "desc" : "MistralAI-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MistralAI-User/1.0; +https://docs.mistral.ai/robots)", + "expect" : + { + "name" : "MistralAI-User", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Perplexity-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)", + "expect" : + { + "name" : "Perplexity-User", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "Pinterestbot", "ua" : "Mozilla/5.0 (compatible; Pinterestbot/1.0; +http://www.pinterest.com/bot.html)", From 29677bcd3d2c0436734b8f54fe6283c054bfd45d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 21 May 2025 09:58:01 +0700 Subject: [PATCH 17/30] Add new browser: `Edge WebView`, `Edge WebView2` --- src/enums/ua-parser-enums.d.ts | 2 ++ src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 19 +++++++++++++++---- test/data/ua/browser/browser-all.json | 20 ++++++++++++++++++++ test/unit/ua-ch.js | 13 +++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index b41dfc4..44fefbf 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -38,6 +38,8 @@ export const Browser: Readonly<{ DUCKDUCKGO: "DuckDuckGo"; ECOSIA: "Ecosia"; EDGE: "Edge"; + EDGE_WEBVIEW: "Edge WebView"; + EDGE_WEBVIEW2: "Edge WebView2"; EPIPHANY: "Epiphany"; FACEBOOK: "Facebook"; FALKON: "Falkon"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 2ae437c..3420acb 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -43,6 +43,8 @@ const Browser = Object.freeze({ DUCKDUCKGO: 'DuckDuckGo', ECOSIA: 'Ecosia', EDGE: 'Edge', + EDGE_WEBVIEW: 'Edge WebView', + EDGE_WEBVIEW2: 'Edge WebView2', EPIPHANY: 'Epiphany', FACEBOOK: 'Facebook', FALKON: 'Falkon', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index ae91dbd..1e6210c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -152,11 +152,11 @@ has = function (str1, str2) { if (typeof str1 === OBJ_TYPE && str1.length > 0) { for (var i in str1) { - if (lowerize(str1[i]) == lowerize(str2)) return true; + if (lowerize(str2) == lowerize(str1[i])) return true; } return false; } - return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; + return isString(str1) ? lowerize(str2) == lowerize(str1) : false; }, isExtensions = function (obj, deep) { for (var prop in obj) { @@ -326,7 +326,9 @@ // Most common regardless engine /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ - /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge + /webview.+edge\/([\w\.]+)/i // Microsoft Edge + ], [VERSION, [NAME, EDGE+' WebView']], [ + /edg(?:e|ios|a)?\/([\w\.]+)/i ], [VERSION, [NAME, 'Edge']], [ // Presto based @@ -441,6 +443,9 @@ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ + /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2 + ], [VERSION, [NAME, EDGE+' WebView2']], [ + / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView ], [[NAME, CHROME+' WebView'], VERSION], [ @@ -1232,10 +1237,16 @@ for (var i in brands) { var brandName = brands[i].brand || brands[i], brandVersion = brands[i].version; - if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) { + if (this.itemType == UA_BROWSER && + !/not.a.brand/i.test(brandName) && + (!prevName || + (/Chrom/.test(prevName) && brandName != CHROMIUM) || + (prevName == EDGE && /WebView2/.test(brandName)) + )) { brandName = strMapper(brandName, { 'Chrome' : 'Google Chrome', 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', 'Chrome WebView' : 'Android WebView', 'Chrome Headless' : 'HeadlessChrome', 'Huawei Browser' : 'HuaweiBrowser', diff --git a/test/data/ua/browser/browser-all.json b/test/data/ua/browser/browser-all.json index 3fc0e5e..9f39de9 100644 --- a/test/data/ua/browser/browser-all.json +++ b/test/data/ua/browser/browser-all.json @@ -2229,6 +2229,26 @@ "major" : "74" } }, + { + "desc" : "Microsoft Edge WebView", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect" : + { + "name" : "Edge WebView", + "version" : "18.17763", + "major" : "18" + } + }, + { + "desc" : "Microsoft Edge WebView2", + "ua" : "Mozilla/5.0 (Linux; Android 11; SM-G991B Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.120 Mobile Safari/537.36 EdgW/1.0", + "expect" : + { + "name" : "Edge WebView2", + "version" : "91.0.4472.120", + "major" : "91" + } + }, { "desc" : "Iridium", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Iridium/43.8 Safari/537.36 Chrome/43.0.2357.132", diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 18dd09a..7d12d50 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -313,6 +313,19 @@ describe('UA-CH Headers tests', () => { } } }, + { + headers : { + 'sec-ch-ua': '" Not;A Brand";v="99", "Microsoft Edge";v="103", "Chromium";v="103", "Microsoft Edge WebView2";v="104"' + }, + expect: { + browser : { + name : 'Edge WebView2', + version : '104', + major : '104', + type : undefined + } + } + }, { headers : { 'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "HuaweiBrowser";v="114"' From a3549efc223e5639af47142125c53224890b8b75 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 30 May 2025 23:16:02 +0700 Subject: [PATCH 18/30] [extensions] Add new bots: Daumoa, iAskBot, Iframely, Qwantbot --- src/extensions/ua-parser-extensions.js | 12 ++++- test/data/ua/extension/crawler.json | 70 ++++++++++++++++++++++++++ test/data/ua/extension/fetcher.json | 10 ++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 8317f4b..a91bdd9 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -48,13 +48,14 @@ const Crawlers = Object.freeze({ // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot + // iAskBot - https://iask.ai // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -68,6 +69,9 @@ const Crawlers = Object.freeze({ // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, + // Daum + /(daum(?:oa)?(?:-image)?)[ \/]([\w\.]+)/i, + // Facebook / Meta // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, @@ -78,6 +82,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // Qwantbot - https://help.qwant.com/bot + /(qwantbot)[-\w]*\/?([\w\.]*)/i, + // SemrushBot - http://www.semrush.com/bot.html /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, @@ -222,10 +229,11 @@ const Fetchers = Object.freeze({ // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan + // Iframely - https://iframely.com/docs/about // Perplexity-User - https://docs.perplexity.ai/guides/bots // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 8ed2d00..bf29428 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -299,6 +299,36 @@ "type" : "crawler" } }, + { + "desc" : "Daum", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant;) Daum 4.1", + "expect" : + { + "name" : "Daum", + "version" : "4.1", + "type" : "crawler" + } + }, + { + "desc" : "Daumoa", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant; not on Windows server;) Daumoa 4.0", + "expect" : + { + "name" : "Daumoa", + "version" : "4.0", + "type" : "crawler" + } + }, + { + "desc" : "Daumoa-image", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant; not on Windows server;) Daumoa-image/1.0", + "expect" : + { + "name" : "Daumoa-image", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Diffbot", "ua" : "Diffbot/0.1", @@ -489,6 +519,16 @@ "type" : "crawler" } }, + { + "desc" : "iAskBot", + "ua" : "Mozilla/5.0 AppleWebKit/605.1.15 (KHTML, like Gecko; compatible; iAskBot/1.0; +https://iask.ai/) Chrome/120.0.6099.119 Safari/605.1.15", + "expect" : + { + "name" : "iAskBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "ImagesiftBot", "ua" : "Mozilla/5.0 (compatible; ImagesiftBot; +imagesift.com)", @@ -620,6 +660,36 @@ "type" : "crawler" } }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot/1.0_12345; +https://help.qwant.com/bot/)", + "expect" : + { + "name" : "Qwantbot", + "version" : "1.0_12345", + "type" : "crawler" + } + }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot-prod51071/1.0; +Qwantbot@qwant.com)", + "expect" : + { + "name" : "Qwantbot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot-news/2.0; +https://help.qwant.com/bot/)", + "expect" : + { + "name" : "Qwantbot", + "version" : "2.0", + "type" : "crawler" + } + }, { "desc" : "SemrushBot", "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 62154a9..4a05393 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -129,6 +129,16 @@ "type" : "fetcher" } }, + { + "desc" : "Iframely", + "ua" : "Iframely/1.3.1 (+https://iframely.com/docs/about)", + "expect" : + { + "name" : "Iframely", + "version" : "1.3.1", + "type" : "fetcher" + } + }, { "desc" : "Meta-ExternalFetcher", "ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", From 72d0c2acb32917a9287b8cb838be015d8e5dcac9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 3 Jun 2025 11:03:45 +0700 Subject: [PATCH 19/30] [extensions] Add new crawler bots: ChatGLM, Onespot, Startpage --- src/extensions/ua-parser-extensions.js | 7 +++--- src/helpers/ua-parser-helpers.js | 3 +++ test/data/ua/extension/crawler.json | 30 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a91bdd9..b2e31fb 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -52,10 +52,11 @@ const Crawlers = Object.freeze({ // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html + // Onespot - https://www.onespot.com/identifying-traffic.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -100,8 +101,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |line|yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 64d7e90..d14667b 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -110,6 +110,9 @@ const isAIBot = (resultOrUA) => [ // You.com 'youbot', + // Zhipu AI + 'chatglm-spider', + // Zyte 'scrapy' diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index bf29428..918ee98 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -259,6 +259,16 @@ "type" : "crawler" } }, + { + "desc" : "ChatGLM-Spider", + "ua" : "Mozilla/5.0 (compatible; ChatGLM-Spider/1.0; +https://chatglm.cn/)", + "expect" : + { + "name" : "ChatGLM-Spider", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Coc Coc Bot (web)", "ua" : "Mozilla/5.0 (compatible; coccocbot-web/1.0; +http://help.coccoc.com/searchengine)", @@ -620,6 +630,16 @@ "type" : "crawler" } }, + { + "desc" : "Onespot", + "ua" : "Mozilla/5.0 (compatible; Onespot-ScraperBot/1.0; +https://www.onespot.com/identifying-traffic.html)", + "expect" : + { + "name" : "Onespot-ScraperBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "OpenAI Search", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot", @@ -750,6 +770,16 @@ "type" : "crawler" } }, + { + "desc" : "Startpage", + "ua" : "StartpagePrivateImageProxy/3.0 (https://www.startpage.com/; support@startpage.com) aiohttp.client/3.11.11", + "expect" : + { + "name" : "StartpagePrivateImageProxy", + "version" : "3.0", + "type" : "crawler" + } + }, { "desc" : "Teoma", "ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)", From 5f1e498bec8751a2017c76e8efc25419a42ce3b7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 6 Jun 2025 22:01:57 +0700 Subject: [PATCH 20/30] Move browser hints map --- src/main/ua-parser.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 1e6210c..b355b80 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -313,6 +313,18 @@ 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined + }, + + browserHintsMap = { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', + 'Chrome WebView': 'Android WebView', + 'Chrome Headless':'HeadlessChrome', + 'Huawei Browser': 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' }; ////////////// @@ -1243,21 +1255,14 @@ (/Chrom/.test(prevName) && brandName != CHROMIUM) || (prevName == EDGE && /WebView2/.test(brandName)) )) { - brandName = strMapper(brandName, { - 'Chrome' : 'Google Chrome', - 'Edge' : 'Microsoft Edge', - 'Edge WebView2' : 'Microsoft Edge WebView2', - 'Chrome WebView' : 'Android WebView', - 'Chrome Headless' : 'HeadlessChrome', - 'Huawei Browser' : 'HuaweiBrowser', - 'MIUI Browser' : 'Miui Browser', - 'Opera Mobi' : 'OperaMobile', - 'Yandex' : 'YaBrowser' - }); - this.set(NAME, brandName) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - prevName = brandName; + brandName = strMapper(brandName, browserHintsMap); + prevName = this.get(NAME); + if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) { + this.set(NAME, brandName) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + prevName = brandName; } if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { this.set(VERSION, brandVersion); From 5e7d0b25a32b7b3303a46919c95c442643a4c05e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 7 Jun 2025 00:05:28 +0700 Subject: [PATCH 21/30] Improve device&OS detection: LG webOS TV --- src/main/ua-parser.js | 49 ++++++++++++++--------- test/data/ua/device/_others.json | 27 +++++++++++++ test/data/ua/device/lg.json | 18 +++++++++ test/data/ua/device/samsung.json | 9 +++++ test/data/ua/os/webos.json | 68 ++++++++++++++++++++++++++++---- 5 files changed, 145 insertions(+), 26 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b355b80..19177da 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -247,17 +247,25 @@ // assign given value, ignore regex match this[q[0]] = q[1]; } - } else if (q.length === 3) { - // check whether function or regex + } else if (q.length >= 3) { + // Check whether q[1] 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; + if (q.length > 3) { + this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; + } else { + // 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; + if (q.length == 3) { + // 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 if (q.length > 4) { + this[q[0]] = match ? q[3].apply(this, [match.replace(q[1], q[2])].concat(q.slice(4))) : 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; @@ -432,7 +440,6 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ - /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /(daum)apps[\/ ]([\w\.]+)/i, // Daum App @@ -440,7 +447,7 @@ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter - /(instagram|snapchat)[\/ ]([-\w\.]+)/i // Instagram/Snapchat + /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna ], [NAME, VERSION, [TYPE, INAPP]], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ @@ -550,7 +557,7 @@ /\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)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, - /samsung[- ]((?!sm-[lr])[-\w]+)/i, + /samsung[- ]((?!sm-[lr]|browser)[-\w]+)/i, /sec-(sgh\w+)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ @@ -607,9 +614,10 @@ /\b(opd2(\d{3}a?))(?: bui|\))/i ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ - // BLU Vivo Series - /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i - ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ + // BLU + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i // Vivo series + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ + // Vivo /; vivo (\w+)(?: bui|\))/i, /\b(v[12]\d{3}\w?[at])(?: bui|;)/i @@ -638,7 +646,7 @@ /((?=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|watch))(\w+)/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ @@ -823,7 +831,7 @@ ], [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]], [ + ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [ // SmartTV from Unidentified Vendors /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i ], [MODEL, [TYPE, SMARTTV]], [ @@ -972,7 +980,7 @@ ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen - /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ @@ -980,9 +988,12 @@ ], [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 + /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS + /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i ], [VERSION, [NAME, 'webOS']], [ + /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i + // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine + ], [[VERSION, strMapper, {'25':'120','24':'108','23':'94','22':'87','6':'79','5':'68','4':'53','3':'38','2':'538','1':'537','*':'TV'}], [NAME, 'webOS']], [ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS ], [VERSION, [NAME, 'watchOS']], [ diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index c58a01e..a270c00 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -290,6 +290,33 @@ "type": "smarttv" } }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 ( DRM; LGE/ATMACA/GRAETZ; GR32S1470; WEBOS22 04.41.53; W22_K8AP; DTV_C22L;) LaTivu_1.0.1_2022", + "expect": { + "vendor": "GRAETZ", + "model": "GR32S1470", + "type": "smarttv" + } + }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 HbbTV/1.5.1 (+DRM; LGE/DUALSHINE/SKYTECH; ST-5090; WEBOS5.0 04.50.63; W50_K6LP; DTV_C20P;)", + "expect": { + "vendor": "SKYTECH", + "model": "ST-5090", + "type": "smarttv" + } + }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 (+DRM; LGE/SILICONPLAYER/Hyundai; 50HYN3205; WEBOS22 04.42.26; W22_K8LP; DTV_C22P;)", + "expect": { + "vendor": "Hyundai", + "model": "50HYN3205", + "type": "smarttv" + } + }, { "desc": "PDA with Windows CE", "ua": "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", diff --git a/test/data/ua/device/lg.json b/test/data/ua/device/lg.json index 9d63ac1..9f125d7 100644 --- a/test/data/ua/device/lg.json +++ b/test/data/ua/device/lg.json @@ -143,6 +143,24 @@ "type": "smarttv" } }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/53.0.2785.34 Safari/537.36 LG Browser/8.00.00(LGE; 32LM627BPSB; 05.40.45; 1; DTV_W19R); webOS.TV-2019; LG NetCast.TV-2013 Compatible (LGE, 32LM627BPSB, wireless)", + "expect": { + "vendor": "LG", + "model": "32LM627BPSB", + "type": "smarttv" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 HbbTV/1.3.1 ( DRM; LGE; OLED55B7V_Z; WEBOS3.5 06.10.60; W3_M16P; ) FVC/2.0 (LGE; WEBOS3.5 ;)", + "expect": { + "vendor": "LG", + "model": "OLED55B7V_Z", + "type": "smarttv" + } + }, { "desc": "LG Android TV", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; LG Android TV Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", diff --git a/test/data/ua/device/samsung.json b/test/data/ua/device/samsung.json index 56e7b73..36b68db 100644 --- a/test/data/ua/device/samsung.json +++ b/test/data/ua/device/samsung.json @@ -296,6 +296,15 @@ "type": "smarttv" } }, + { + "desc": "Samsung SmartTV", + "ua": "Mozilla/5.0 (SMART-TV; Linux; Tizen 8.0) AppleWebKit/537.36 (KHTML, like Gecko) Samsung Browser/7.0 Chrome/108.0.5359.1 TV Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "undefined", + "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", diff --git a/test/data/ua/os/webos.json b/test/data/ua/os/webos.json index c31cd3f..7f3d04a 100644 --- a/test/data/ua/os/webos.json +++ b/test/data/ua/os/webos.json @@ -1,6 +1,6 @@ [ { - "desc" : "WebOS", + "desc" : "HP WebOS", "ua" : "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.83 Safari/534.6 TouchPad/1.0", "expect" : { @@ -9,7 +9,7 @@ } }, { - "desc" : "WebOS", + "desc" : "Palm WebOS", "ua" : "Mozilla/5.0 (webOS/1.4.5; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0", "expect" : { @@ -17,13 +17,58 @@ "version" : "1.4.5" } }, + { + "desc" : "WebOS TV 22", + "ua" : " Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/87.0.4280.88 Safari/537.36 LG Browser/8.00.00(LGE; ST50K-LG2200WEB; 04.42.26; 0x00000001; DTV_C22P); webOS.TV-2022; LG NetCast.TV-2013 Compatible (LGE, ST50K-LG2200WEB, wireless)", + "expect" : + { + "name" : "webOS", + "version" : "22" + } + }, + { + "desc" : "WebOS TV 22", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 (+DRM; LGE/SILICONPLAYER/Hyundai; 50HYN3205; WEBOS22 04.42.26; W22_K8LP; DTV_C22P;)", + "expect" : + { + "name" : "webOS", + "version" : "22" + } + }, + { + "desc" : "WebOS TV 5.0", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/68.0.3440.106 Safari/537.36 LG Browser/8.00.00(LGE; SWU-6522; 04.50.63; 0x00000001; DTV_C20P); webOS.TV-2020; LG NetCast.TV-2013 Compatible (LGE, SWU-6522, wireless)", + "expect" : + { + "name" : "webOS", + "version" : "5" + } + }, + { + "desc" : "WebOS TV 5.0", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 HbbTV/1.5.1 (+DRM; LGE/WALTON/Hyundai; 50HYN3205; WEBOS5.0 04.50.63; W50_K6LP; DTV_C20P;)", + "expect" : + { + "name" : "webOS", + "version" : "5.0" + } + }, { "desc" : "WebOS TV 5.x", "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 WebAppManager", "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "5" + } + }, + { + "desc" : "WebOS TV 5.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/68.0.3440.106 Safari/537.36 LG Browser/8.00.00(LGE; E65A71B-S; 04.50.63; 0x00000001; DTV_C20P); webOS.TV-2020; LG NetCast.TV-2013 Compatible (LGE, E65A71B-S, wired)", + "expect" : + { + "name" : "webOS", + "version" : "5" } }, { @@ -32,7 +77,16 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "4" + } + }, + { + "desc" : "WebOS TV 3.5", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 HbbTV/1.3.1 ( DRM; LGE; OLED55B7V-T; WEBOS3.5 06.10.60; W3_M16P; ) FVC/2.0 (LGE; WEBOS3.5 ;)", + "expect" : + { + "name" : "webOS", + "version" : "3.5" } }, { @@ -41,7 +95,7 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "3" } }, { @@ -50,7 +104,7 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "2" } }, { @@ -59,7 +113,7 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "1" } } ] \ No newline at end of file From e31229828318b813e99b3598928d6aa69b8d14ca Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 10 Jun 2025 23:03:16 +0700 Subject: [PATCH 22/30] Fix #794: Detect Windows 7 --- src/main/ua-parser.js | 2 +- test/data/ua/os/windows.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 19177da..43f6a1a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -943,7 +943,7 @@ os : [[ // Windows - /microsoft (windows) (vista|xp)/i // Windows (iTunes) + /microsoft (windows) (7|vista|xp)/i // Windows ], [NAME, VERSION], [ /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index 39e9541..1173baf 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -53,6 +53,15 @@ "version" : "Vista" } }, + { + "desc" : "Windows 7", + "ua" : "Microsoft Windows 7", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, { "desc" : "Windows 7", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", From 1282175bac96e6be108be067ce451f59d37c0d85 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 10 Jun 2025 23:05:03 +0700 Subject: [PATCH 23/30] [extensions] Add new libraries: AdobeAIR, aiohttp, nutch, httpx, urllib3 --- src/extensions/ua-parser-extensions.js | 5 +-- test/data/ua/extension/library.json | 50 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index b2e31fb..7fff4c0 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -339,8 +339,9 @@ const Libraries = Object.freeze({ browser : [ // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent [ - /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, - /(jsdom)\/([\w\.]+)/i, + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i, + /(adobeair|aiohttp|jsdom)\/([\w\.]+)/i, + /(nutch)-([\w\.-]+)(\(|$)/i, /\((java)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, LIBRARY]] ] diff --git a/test/data/ua/extension/library.json b/test/data/ua/extension/library.json index f691105..17435a2 100644 --- a/test/data/ua/extension/library.json +++ b/test/data/ua/extension/library.json @@ -1,4 +1,24 @@ [ + { + "desc" : "AdobeAIR", + "ua" : "Mozilla/5.0 (Windows; U; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) AdobeAIR/3.1", + "expect" : + { + "name" : "AdobeAIR", + "version" : "3.1", + "type" : "library" + } + }, + { + "desc" : "aiohttp", + "ua" : "Python/3.9 aiohttp/3.8.1", + "expect" : + { + "name" : "aiohttp", + "version" : "3.8.1", + "type" : "library" + } + }, { "desc" : "Apache-HttpClient", "ua" : "Apache-HttpClient/4.5.14 (Java/17.0.12)", @@ -109,6 +129,16 @@ "type" : "library" } }, + { + "desc" : "Nutch", + "ua" : "AliyunSecBot/Nutch-1.21-SNAPSHOT", + "expect" : + { + "name" : "Nutch", + "version" : "1.21-SNAPSHOT", + "type" : "library" + } + }, { "desc" : "OkHttp", "ua" : "okhttp/3.2.0", @@ -149,6 +179,16 @@ "type" : "library" } }, + { + "desc" : "Python httpx", + "ua" : "python-httpx/0.27.2", + "expect" : + { + "name" : "python-httpx", + "version" : "0.27.2", + "type" : "library" + } + }, { "desc" : "Python urllib", "ua" : "Python-urllib/2.6", @@ -159,6 +199,16 @@ "type" : "library" } }, + { + "desc" : "Python urllib3", + "ua" : "python-urllib3/1.26.18", + "expect" : + { + "name" : "python-urllib3", + "version" : "1.26.18", + "type" : "library" + } + }, { "desc" : "Python requests", "ua" : "python-requests/2.32", From d28d74fea7864888747b4a7cce73e8e84c50467d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 17 Jun 2025 01:47:24 +0700 Subject: [PATCH 24/30] [extensions] Add new apps: Discord, Evernote, Figma, Flipboard, Mattermost, Notion, Postman, Rambox, Rocket.Chat, Teams, TikTok Lite, VS Code --- src/extensions/ua-parser-extensions.js | 26 +++- test/data/ua/extension/inapp.json | 170 +++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 7fff4c0..345c882 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -268,13 +268,31 @@ const Fetchers = Object.freeze({ /////////////////// const InApps = Object.freeze({ - browser : [ + browser : [[ + // Discord/Figma/Flipboard/Mattermost/Notion/Postman/Rambox/Rocket.Chat/Slack/Teams + /\b(discord|figma|mattermost|notion|postman|rambox|rocket.chat|slack|teams)\/([\w\.]+).+(electron\/|; ios)/i, + /(flipboard)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, INAPP]], [ + + // Evernote/Teams on mobile + /(evernote) win/i, + /(teams)mobile-(ios|and)/i + ], [NAME, [TYPE, INAPP]], [ + // Slack - [/(?:slack(?=.+electron|.+ios)|chatlyio)\/([\d\.]+)/i], - [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], + /chatlyio\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], [ + + // TikTok Lite + /ultralite app_version\/([\w\.]+)/i], + [VERSION, [NAME, 'TikTok Lite'], [TYPE, INAPP]], [ + + // VS Code + /\) code\/([\d\.]+).+electron\//i], + [VERSION, [NAME, 'VS Code'], [TYPE, INAPP]], [ // Yahoo! Japan - [/jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + /jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] ] }); diff --git a/test/data/ua/extension/inapp.json b/test/data/ua/extension/inapp.json index 5512465..f1861c4 100644 --- a/test/data/ua/extension/inapp.json +++ b/test/data/ua/extension/inapp.json @@ -1,4 +1,154 @@ [ + { + "desc" : "Discord on Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) discord/0.0.26 Chrome/108.0.5359.215 Electron/22.3.2 Safari/537.36", + "expect" : + { + "name" : "discord", + "version" : "0.0.26", + "type" : "inapp" + } + }, + { + "desc" : "Discord on iPad", + "ua" : "Discord/52.0 (iPad; iOS 14.4; Scale/2.00)", + "expect" : + { + "name" : "Discord", + "version" : "52.0", + "type" : "inapp" + } + }, + { + "desc" : "Evernote on Windows", + "ua" : "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Evernote Windows/306387 (pt-PT, DDL); Windows/6.1.0 (Win32); Safari/537.36", + "expect" : + { + "name" : "Evernote", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Figma on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) Figma/99.0.0 Chrome/89.0.4389.128 Electron/12.0.9 Safari/537.36", + "expect" : + { + "name" : "Figma", + "version" : "99.0.0", + "type" : "inapp" + } + }, + { + "desc" : "Flipboard on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.0.0; SM-A720F Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36 Flipboard/4.1.13/4342,4.1.13.4342", + "expect" : + { + "name" : "Flipboard", + "version" : "4.1.13", + "type" : "inapp" + } + }, + { + "desc" : "Mattermost on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Mattermost/4.4.0 Chrome/76.0.3809.146 Electron/6.1.7 Safari/537.36", + "expect" : + { + "name" : "Mattermost", + "version" : "4.4.0", + "type" : "inapp" + } + }, + { + "desc" : "Mattermost on iPad", + "ua" : "Mattermost/1.49.1 (iPad; iOS 15.3.1; Scale/2.00)", + "expect" : + { + "name" : "Mattermost", + "version" : "1.49.1", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Teams/1.8.00.3758 Chrome/126.0.6478.261 Electron/31.7.7 Safari/537.36", + "expect" : + { + "name" : "Teams", + "version" : "1.8.00.3758", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on iPad", + "ua" : "Mozilla/5.0 (iPad; CPU OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 TeamsMobile-iOS", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on iPhone", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 TeamsMobile-iOS", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 TeamsMobile-Android", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Notion on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Notion/2.0.23 Chrome/91.0.4472.164 Electron/13.6.9 Safari/537.36", + "expect" : + { + "name" : "Notion", + "version" : "2.0.23", + "type" : "inapp" + } + }, + { + "desc" : "Postman on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Postman/9.29.0 Chrome/94.0.4606.81 Electron/15.5.7 Safari/537.36", + "expect" : + { + "name" : "Postman", + "version" : "9.29.0", + "type" : "inapp" + } + }, + { + "desc" : "Rambox on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Rambox/0.7.7 Chrome/78.0.3904.130 Electron/7.2.4 Safari/537.36", + "expect" : + { + "name" : "Rambox", + "version" : "0.7.7", + "type" : "inapp" + } + }, + { + "desc" : "Rocket.Chat on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Rocket.Chat/3.8.16 Chrome/106.0.5249.199 Electron/21.3.3 Safari/537.36", + "expect" : + { + "name" : "Rocket.Chat", + "version" : "3.8.16", + "type" : "inapp" + } + }, { "desc" : "Slack on mac", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Slack/4.39.90 Chrome/127.0.6533.72 Electron/13.1.9 Safari/537.36", @@ -9,6 +159,26 @@ "type" : "inapp" } }, + { + "desc" : "TikTok Lite", + "ua" : "Mozilla/5.0 (Linux; Android 8.0.0; SM-J400F Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.91 Mobile Safari/537.36 Channel/release AppName/ultralite app_version/27.2.3 Region/ID ByteLocale/id-ID ByteFullLocale/id-ID", + "expect" : + { + "name" : "TikTok Lite", + "version" : "27.2.3", + "type" : "inapp" + } + }, + { + "desc" : "VS Code on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.85.1 Chrome/114.0.5735.289 Electron/25.9.7 Safari/537.36", + "expect" : + { + "name" : "VS Code", + "version" : "1.85.1", + "type" : "inapp" + } + }, { "desc" : "Yahoo! Japan on Android", "ua" : "Mozilla/5.0 (Linux; Android 13; SH-M20 Build/TKQ1.220915.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36 YJApp-ANDROID jp.co.yahoo.android.yjtop/3.187.0", From ee88f16620cb04723fb721b7112f0a7f408822e0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 18 Jun 2025 19:01:12 +0700 Subject: [PATCH 25/30] [extensions] Add new email: Apple's Mail, DaumMail, Polymail, ProtonMail, SparkDesktop, Zimbra, ZohoMail-Desktop --- src/extensions/ua-parser-extensions.js | 15 ++++-- test/data/ua/extension/email.json | 70 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 345c882..d89b7ec 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -212,9 +212,18 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ [ - // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i - ], [NAME, VERSION, [TYPE, EMAIL]] + // Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird + // Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail + // ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop + /((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i, + + // Apple's Mail + /(mail)\/([\w\.]+) cf/i + ], [NAME, VERSION, [TYPE, EMAIL]], [ + + // Zimbra + /zdesktop\/([\w\.]+)/i + ], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]] ] }); diff --git a/test/data/ua/extension/email.json b/test/data/ua/extension/email.json index 27fbce5..8acd5d4 100644 --- a/test/data/ua/extension/email.json +++ b/test/data/ua/extension/email.json @@ -9,6 +9,16 @@ "type" : "email" } }, + { + "desc" : "Apple Mail", + "ua" : "Mail/3826.500.181.1.5 CFNetwork/3826.500.111.1.1 Darwin/24.4.0", + "expect" : + { + "name" : "Mail", + "version" : "3826.500.181.1.5", + "type" : "email" + } + }, { "desc" : "BlueMail", "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlueMail/0.10.31 Chrome/61.0.3163.100 Electron/2.0.18 Safari/537.36", @@ -29,6 +39,16 @@ "type" : "email" } }, + { + "desc" : "DaumMail", + "ua" : "DaumMobileApp (LG-KU5400; U; Android 2.3.7|10; ko-kr) DaumMail/1.0.8 ", + "expect" : + { + "name" : "DaumMail", + "version" : "1.0.8", + "type" : "email" + } + }, { "desc" : "Evolution", "ua" : "Evolution/3.52.3", @@ -119,6 +139,36 @@ "type" : "email" } }, + { + "desc" : "Polymail", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Polymail/2.3.12 Chrome/114.0.5735.134 Electron/25.2.0 Safari/537.36", + "expect" : + { + "name" : "Polymail", + "version" : "2.3.12", + "type" : "email" + } + }, + { + "desc" : "ProtonMail", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ProtonMail/1.4.0 Chrome/122.0.6261.156 Electron/29.4.6 Safari/537.36", + "expect" : + { + "name" : "ProtonMail", + "version" : "1.4.0", + "type" : "email" + } + }, + { + "desc" : "SparkDesktop", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) SparkDesktop/3.0.30 Chrome/102.0.5005.63 Electron/19.0.4 Safari/537.36", + "expect" : + { + "name" : "SparkDesktop", + "version" : "3.0.30", + "type" : "email" + } + }, { "desc" : "Sparrow", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Sparrow/1043.1", @@ -158,5 +208,25 @@ "version" : "1.3.10", "type" : "email" } + }, + { + "desc" : "Zimbra", + "ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.2; it; rv:1.9.2.19pre) Gecko/20110902 Prism zdesktop/7.2.8", + "expect" : + { + "name" : "Zimbra", + "version" : "7.2.8", + "type" : "email" + } + }, + { + "desc" : "ZohoMail", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ZohoMail-Desktop/1.6.3 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36", + "expect" : + { + "name" : "ZohoMail-Desktop", + "version" : "1.6.3", + "type" : "email" + } } ] From f300478bdc8ccdbabfbb2162d6f045525d94d281 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 19 Jun 2025 00:14:25 +0700 Subject: [PATCH 26/30] Identify device that uses Firefox Reality / Wolvic as an XR device --- src/main/ua-parser.js | 4 +++- test/data/ua/device/_others.json | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 43f6a1a..f34933b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -886,6 +886,8 @@ ], [VENDOR, MODEL, [TYPE, XR]], [ /(quest( \d| pro)?s?).+vr/i // Meta Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ + /mobile vr; rv.+firefox/i // Unidentifiable VR device using Firefox Reality / Wolvic + ], [[TYPE, XR]], [ /////////////////// // EMBEDDED @@ -897,7 +899,7 @@ ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ /(homepod).+mac os/i // Apple HomePod ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ - /windows iot/i + /windows iot/i // Unidentifiable embedded device using Windows IoT ], [[TYPE, EMBEDDED]], [ //////////////////// diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index a270c00..55329b3 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -290,6 +290,24 @@ "type": "smarttv" } }, + { + "desc": "Unknown VR Device using Firefox Reality", + "ua": "Mozilla/5.0 (Android 10; Mobile VR; rv:123.0) Gecko/123.0 Firefox/123.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "xr" + } + }, + { + "desc": "Unknown VR Device using Wolvic", + "ua": "Mozilla/5.0 (Android 14; Mobile VR; rv:128.0) Gecko/128.0 Firefox/128.0 Wolvic/1.8", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "xr" + } + }, { "desc": "Smart TV", "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 ( DRM; LGE/ATMACA/GRAETZ; GR32S1470; WEBOS22 04.41.53; W22_K8AP; DTV_C22L;) LaTivu_1.0.1_2022", From f9836f198199fc3d57542e1517a75dcc052331c9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 19 Jun 2025 16:14:09 +0700 Subject: [PATCH 27/30] Add new vendor: Retroid --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 11 ++++++----- test/data/ua/device/retroid.json | 11 +++++++++++ 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 test/data/ua/device/retroid.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 44fefbf..2b2b466 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -244,6 +244,7 @@ export const Vendor: Readonly<{ PICO: "Pico"; POLYTRON: "Polytron"; REALME: "Realme"; + RETROID: "Retroid"; RIM: "RIM"; ROKU: "Roku"; SAMSUNG: "Samsung"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 3420acb..c4d869c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -255,6 +255,7 @@ const Vendor = Object.freeze({ PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', + RETROID: 'Retroid', RIM: 'RIM', ROKU: 'Roku', SAMSUNG: 'Samsung', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index f34933b..69c4485 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -842,15 +842,16 @@ // CONSOLES /////////////////// - /(ouya)/i, // Ouya - /(nintendo) (\w+)/i // Nintendo - ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ - /droid.+; (shield)( bui|\))/i // Nvidia Portable - ], [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]], [ + /(ouya)/i, // Ouya + /(nintendo) (\w+)/i, // Nintendo + /(retroid) (pocket ([^\)]+))/i // Retroid Pocket + ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /////////////////// // WEARABLES diff --git a/test/data/ua/device/retroid.json b/test/data/ua/device/retroid.json new file mode 100644 index 0000000..fc7a83d --- /dev/null +++ b/test/data/ua/device/retroid.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Retroid Pocket 4 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 13; Retroid Pocket 4 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.96 YaBrowser/24.4.3.96.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Retroid", + "model": "Pocket 4 Pro", + "type": "console" + } + } +] \ No newline at end of file From a9378427eddb7a49395be6556c448b95814352fc Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 21 Jun 2025 13:31:52 +0700 Subject: [PATCH 28/30] Improve Windows detection & recognize Windows CE & RT as separate OS variants --- src/enums/ua-parser-enums.d.ts | 2 + src/enums/ua-parser-enums.js | 2 + src/main/ua-parser.js | 39 ++-- test/data/ua/os/windows-ce.json | 20 ++ test/data/ua/os/windows-mobile.json | 18 ++ test/data/ua/os/windows-phone.json | 31 ++- test/data/ua/os/windows-rt.json | 20 ++ test/data/ua/os/windows.json | 290 ++++++++++++++++++++++++---- 8 files changed, 365 insertions(+), 57 deletions(-) create mode 100644 test/data/ua/os/windows-ce.json create mode 100644 test/data/ua/os/windows-rt.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 2b2b466..97c760a 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -373,9 +373,11 @@ export const OS: Readonly<{ WATCHOS: "watchOS"; WEBOS: "WebOS"; WINDOWS: "Windows"; + WINDOWS_CE: "Windows CE"; WINDOWS_IOT: "Windows IoT"; WINDOWS_MOBILE: "Windows Mobile"; WINDOWS_PHONE: "Windows Phone"; + WINDOWS_RT: "Windows RT"; XBOX: "Xbox"; XUBUNTU: "Xubuntu"; ZENWALK: "Zenwalk"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index c4d869c..803970c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -388,9 +388,11 @@ const OS = Object.freeze({ WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', + WINDOWS_CE: 'Windows CE', WINDOWS_IOT: 'Windows IoT', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', + WINDOWS_RT: 'Windows RT', XBOX: 'Xbox', XUBUNTU: 'Xubuntu', ZENWALK: 'Zenwalk' diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 69c4485..e23ebb5 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -299,17 +299,17 @@ ////////////// var 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' + 'ME' : '4.90', + 'NT 3.51': '3.51', + 'NT 4.0': '4.0', + '2000' : ['5.0', '5.01'], + 'XP' : ['5.1', '5.2'], + 'Vista' : '6.0', + '7' : '6.1', + '8' : '6.2', + '8.1' : '6.3', + '10' : ['6.4', '10.0'], + 'NT' : '' }, formFactorsMap = { @@ -946,14 +946,17 @@ os : [[ // Windows - /microsoft (windows) (7|vista|xp)/i // Windows + /(windows nt) (6\.[23]); arm/i // Windows RT + ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ + /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone + // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|vista|xp)/i + ], [NAME, VERSION], [ + /windows nt ?([\d\.\)]*)(?!.+xbox)/i, + /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i + ], [[VERSION, /(;|\))/g, '', strMapper, windowsVersionMap], [NAME, WINDOWS]], [ + /(windows ce)\/?([\d\.]*)/i // Windows CE ], [NAME, VERSION], [ - /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone - ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /windows nt 6\.2; (arm)/i, // Windows RT - /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i, - /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i - ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS diff --git a/test/data/ua/os/windows-ce.json b/test/data/ua/os/windows-ce.json new file mode 100644 index 0000000..a28eb17 --- /dev/null +++ b/test/data/ua/os/windows-ce.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Windows CE", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", + "expect" : + { + "name" : "Windows CE", + "version" : "undefined" + } + }, + { + "desc" : "Windows CE", + "ua" : "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", + "expect" : + { + "name" : "Windows CE", + "version" : "1.0.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows-mobile.json b/test/data/ua/os/windows-mobile.json index 5baf78c..78df88b 100644 --- a/test/data/ua/os/windows-mobile.json +++ b/test/data/ua/os/windows-mobile.json @@ -16,5 +16,23 @@ "name" : "Windows Mobile", "version" : "undefined" } + }, + { + "desc" : "Windows Mobile", + "ua" : "Opera/9.7 (Windows Mobile; PPC; Opera Mobi/35166; U; en) Presto/2.2.1", + "expect" : + { + "name" : "Windows Mobile", + "version" : "undefined" + } + }, + { + "desc" : "Windows Mobile 6.1", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11) 320x240; VZW; Motorola-Q9c; Windows Mobile 6.1 Standard", + "expect" : + { + "name" : "Windows Mobile", + "version" : "6.1" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/windows-phone.json b/test/data/ua/os/windows-phone.json index a6a8b82..bf4bd0a 100644 --- a/test/data/ua/os/windows-phone.json +++ b/test/data/ua/os/windows-phone.json @@ -9,14 +9,32 @@ } }, { - "desc" : "Windows Phone OS", + "desc" : "Windows Phone 6.5", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; HTC_Touch2_T3333; Windows Phone 6.5)", + "expect" : + { + "name" : "Windows Phone", + "version" : "6.5" + } + }, + { + "desc" : "Windows Phone 7.0", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; DELL; Venue Pro)", "expect" : { - "name" : "Windows Phone OS", + "name" : "Windows Phone", "version" : "7.0" } }, + { + "desc" : "Windows Phone 7.5", + "ua" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; NOKIA; Lumia 800)", + "expect" : + { + "name" : "Windows Phone", + "version" : "7.5" + } + }, { "desc" : "Windows Phone 8", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)", @@ -25,5 +43,14 @@ "name" : "Windows Phone", "version" : "8.0" } + }, + { + "desc" : "Windows Phone 8.1", + "ua" : "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", + "expect" : + { + "name" : "Windows Phone", + "version" : "8.1" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/windows-rt.json b/test/data/ua/os/windows-rt.json new file mode 100644 index 0000000..8bee151 --- /dev/null +++ b/test/data/ua/os/windows-rt.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Windows RT 8", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", + "expect" : + { + "name" : "Windows RT", + "version" : "8" + } + }, + { + "desc" : "Windows RT 8.1", + "ua" : "Mozilla/5.0 (Windows NT 6.3; ARM; Trident/7.0; Touch; rv:11.0) like Gecko", + "expect" : + { + "name" : "Windows RT", + "version" : "8.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index 1173baf..f2f747d 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -1,4 +1,103 @@ [ + { + "desc" : "Windows 3.1", + "ua" : "NCSA_Mosaic/2.0 (Windows 3.1)", + "expect" : + { + "name" : "Windows", + "version" : "3.1" + } + }, + { + "desc" : "Windows 3.1", + "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 3.1)", + "expect" : + { + "name" : "Windows", + "version" : "3.1" + } + }, + { + "desc" : "Windows NT", + "ua" : "Mozilla/4.51 [de] (WinNT; I)", + "expect" : + { + "name" : "Windows", + "version" : "NT" + } + }, + { + "desc" : "Windows NT 3.51", + "ua" : "Mozilla/4.0 (compatible; MSIE 4.0; Windows NT)", + "expect" : + { + "name" : "Windows", + "version" : "NT" + } + }, + { + "desc" : "Windows NT 3.51", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.05; Windows NT 3.51)", + "expect" : + { + "name" : "Windows", + "version" : "NT 3.51" + } + }, + { + "desc" : "Windows NT 4.0", + "ua" : "Opera/8.41.(Windows NT 4.0; ts-ZA) Presto/2.9.178 Version/11.00", + "expect" : + { + "name" : "Windows", + "version" : "NT 4.0" + } + }, + { + "desc" : "Windows NT 4.0", + "ua" : "Mozilla/5.0 (Windows; U; WinNT4.0; de-DE; rv:1.7.5) Gecko/20041108 Firefox/52.7.4", + "expect" : + { + "name" : "Windows", + "version" : "NT 4.0" + } + }, + { + "desc" : "Netscape on Windows 95", + "ua" : "Mozilla/5.0 (Windows; U; Win95; de-DE; rv:0.9.2) Gecko/20010726 Netscape6/6.1", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/3.0 (Win95)", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/3.0 (compatible; Opera/3.0; Windows 95/NT4) 3.2", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0; Windows 95) Opera 6.02 [en]", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, { "desc" : "Windows 95", "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)", @@ -17,6 +116,24 @@ "version" : "98" } }, + { + "desc" : "Firebird on Windows 98", + "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031007 Firebird/0.7", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, + { + "desc" : "K-Meleon on Windows 98", + "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, { "desc" : "Windows ME", "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90) Gecko/20020502 CS 2000 7.0/7.0", @@ -26,6 +143,51 @@ "version" : "ME" } }, + { + "desc" : "Opera on Windows ME", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0; Windows ME) Opera 5.12 [de]", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Netscape on Windows ME", + "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90; en-US; rv:1.8.1.8pre) Gecko/20071015 Firefox/2.0.0.7 Navigator/9.0", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Netscape on Windows 2000", + "ua" : "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.5) Gecko/20050519 Netscape/8.0.1", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Opera on Windows 2000", + "ua" : "Opera/6.05 (Windows 2000; U)", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Opera on Windows 2000", + "ua" : "Opera/9.69 (Windows NT 5.01; en-US) Presto/2.8.160 Version/10.00", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, { "desc" : "Windows 2000", "ua" : "Mozilla/3.0 (compatible; MSIE 3.0; Windows NT 5.0)", @@ -44,6 +206,15 @@ "version" : "XP" } }, + { + "desc" : "Windows XP", + "ua" : "Mozilla/5.0 (Windows XP; U) Opera 6.05 [de]", + "expect" : + { + "name" : "Windows", + "version" : "XP" + } + }, { "desc" : "Windows Vista", "ua" : "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR)", @@ -62,6 +233,15 @@ "version" : "7" } }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Evernote Windows/306387 (pt-PT, DDL); Windows/6.1.0 (Win32); Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, { "desc" : "Windows 7", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", @@ -80,6 +260,15 @@ "version" : "8" } }, + { + "desc" : "Windows 8.1", + "ua" : "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:66.0.5) Gecko/20100101 Firefox/66.0.5", + "expect" : + { + "name" : "Windows", + "version" : "8.1" + } + }, { "desc" : "Windows 10", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", @@ -108,48 +297,75 @@ } }, { - "desc" : "Windows RT", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", - "expect" : - { - "name" : "Windows", - "version" : "RT" - } - }, - { - "desc" : "Windows CE", - "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", - "expect" : - { - "name" : "Windows", - "version" : "CE" - } - }, - { - "desc" : "Windows NT on x86 or aarch64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Windows", - "version" : "NT x" - } - }, - { - "desc" : "Windows NT on x64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Windows", - "version" : "NT x" - } - }, - { - "desc" : "iTunes Windows Vista", + "desc" : "iTunes on Windows Vista", "ua" : "iTunes/10.7 (Windows; Microsoft Windows Vista Home Premium Edition Service Pack 1 (Build 6001)) AppleWebKit/536.26.9", "expect" : { "name" : "Windows", "version" : "Vista" } + }, + { + "desc" : "iTunes on Windows 7", + "ua" : "iTunes/10.6.3 (Windows; Microsoft Windows 7 x64 Business Edition Service Pack 1 (Build 7601)) AppleWebKit/534.57.2", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "iTunes on Windows 8", + "ua" : "iTunes/12.1.1 (Windows; Microsoft Windows 8 x64 Business Edition (Build 9200)) AppleWebKit/7600.1017.9000.2", + "expect" : + { + "name" : "Windows", + "version" : "8" + } + }, + { + "desc" : "iTunes on Windows 8.1", + "ua" : "iTunes/12.4 (Windows; Microsoft Windows 8.1 x64 Business Edition (Build 9200); x64) AppleWebKit/7601.6016.1000.1", + "expect" : + { + "name" : "Windows", + "version" : "8.1" + } + }, + { + "desc" : "iTunes on Windows 10", + "ua" : "iTunes/12.9.1 (Windows; Microsoft Windows 10 x64 Professional Edition (Build 18362); x64) AppleWebKit/7606.2104.0.21", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 10", + "ua" : "iTunes/12.6.3 (Windows; Microsoft Windows 10.0 x64 (Build 17763); x64) AppleWebKit/7604.1038.1006.6", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 10 S", + "ua" : "iTunes/12.12 (Windows; Microsoft Windows 10 S x64; x64) AppleWebKit/7613.2007", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 11", + "ua" : "iTunes/12.13 (Windows; Microsoft Windows 11 x64; x64) AppleWebKit/7613.2007", + "expect" : + { + "name" : "Windows", + "version" : "11" + } } ] \ No newline at end of file From 86b3cd37e25d1958f9a59054b6d3afb3abf8a2a5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 21 Jun 2025 21:37:07 +0700 Subject: [PATCH 29/30] Improve OS detection: Windows Server series --- src/main/ua-parser.js | 2 +- test/data/ua/os/windows.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index e23ebb5..e3373d2 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -950,7 +950,7 @@ ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 - /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|vista|xp)/i + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|server 20\d\d( r2)?|vista|xp)/i ], [NAME, VERSION], [ /windows nt ?([\d\.\)]*)(?!.+xbox)/i, /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index f2f747d..faa9652 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -242,6 +242,15 @@ "version" : "7" } }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (Windows 7 Enterprise; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6099.71 Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, { "desc" : "Windows 7", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", @@ -278,6 +287,15 @@ "version" : "10" } }, + { + "desc" : "Windows Server 2012 R2", + "ua" : "Mozilla/5.0 (Windows Server 2012 R2 Standard; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5975.80 Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "Server 2012 R2" + } + }, { "desc" : "WeChat Desktop for Windows Built-in Browser", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", From c9d008e97af92aa860f3361964e1fb0e485fca34 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 21 Jun 2025 21:41:00 +0700 Subject: [PATCH 30/30] Bump version `2.0.4` --- CHANGELOG.md | 21 ++ dist/ua-parser.min.js | 4 +- dist/ua-parser.min.mjs | 4 +- dist/ua-parser.pack.js | 4 +- dist/ua-parser.pack.mjs | 4 +- package-lock.json | 4 +- package.json | 2 +- src/enums/ua-parser-enums.d.ts | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 14 +- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 68 +++++-- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 5 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 237 +++++++++++++---------- 19 files changed, 251 insertions(+), 134 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2fa9b..d1f9ac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,27 @@ --- +## Version 2.0.4 + +- Add new browser: Edge WebView, Edge WebView2 +- Add new device vendor: Lava, Retroid, Vizio +- Add new OS: ArcaOS, Knoppix, Xubuntu, Windows CE, Windows RT +- Improve device detection: Google Pixel & Pixelbook Series, HMD, Infinix, LG WebOS TV, Motorola, Nothing, OnePlus, Sony, Tecno +- Improve OS detection: AIX, Arch, Fuchsia, Haiku, HarmonyOS, Mint, MorphOS, Solaris, Windows +- Improve `withClientHints()` browser naming adjustments: `Microsoft Edge WebView2` => `Edge WebView2` +- Identify device that uses Firefox Reality / Wolvic as `xr` +- Identify device with large screen as `smarttv` +- Identify Windows CE & Windows RT as distinct OS variants +- `extensions` submodule: + - Remove lookbehind assertion to ensure regex compatibility + - Add new crawlers: ChatGLM, Daum, iAskBot, Onespot, Qwantbot, Startpage + - Add new emails: Apple's Mail, DaumMail, Polymail, ProtonMail, SparkDesktop, Zimbra, ZohoMail-Desktop + - Add new fetchers: Iframely, MistralAI-User, Perplexity-User + - Add new inApps: Discord, Evernote, Figma, Flipboard, Mattermost, Notion, Postman, Rambox, Rocket.Chat, Microsoft Teams, TikTok Lite, VS Code + - Add new libraries: AdobeAIR, aiohttp, nutch, httpx, urllib3 +- `enum` submodule: + - Fix mistakenly placed `BLU` categorized as browser name instead of device vendor + ## Version 2.0.3 - Add new browser: Dooble, Ecosia, LG Browser, Otter, qutebrowser, Surf diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 0c7f407..7d65516 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.3",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome","Huawei Browser":"HuaweiBrowser","MIUI Browser":"Miui Browser","Opera Mobi":"OperaMobile",Yandex:"YaBrowser"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,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(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){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 if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/Chrom/.test(prevName)&&brandName!=CHROMIUM||prevName==EDGE&&/WebView2/.test(brandName))){brandName=strMapper(brandName,browserHintsMap);prevName=this.get(NAME);if(!(prevName&&!/Chrom/.test(prevName)&&/Chrom/.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,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(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs index b86273f..3cb0831 100644 --- a/dist/ua-parser.min.mjs +++ b/dist/ua-parser.min.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -var LIBVERSION="2.0.3",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome","Huawei Browser":"HuaweiBrowser","MIUI Browser":"Miui Browser","Opera Mobi":"OperaMobile",Yandex:"YaBrowser"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,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(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file +var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){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 if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/Chrom/.test(prevName)&&brandName!=CHROMIUM||prevName==EDGE&&/WebView2/.test(brandName))){brandName=strMapper(brandName,browserHintsMap);prevName=this.get(NAME);if(!(prevName&&!/Chrom/.test(prevName)&&/Chrom/.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,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(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index ab102f1..94f5cc5 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -((i,l)=>{function U(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ii,e):Ii,j.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Bi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Bi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>E?Hi(i,E):i),this}]]).setUA(r),this):new I(i,e,t).getResult()}I.VERSION="2.0.3",I.BROWSER=U([v,y,L,k]),I.CPU=U([C]),I.DEVICE=U([T,x,k,G,S,e,r,t,D]),I.ENGINE=I.OS=U([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=I:exports).UAParser=I:typeof define===R&&define.amd?define(function(){return I}):Ti&&(i.UAParser=I);var Ri,Vi=Ti&&(i.jQuery||i.Zepto);Vi&&!Vi.ua&&(Ri=new I,Vi.ua=Ri.getResult(),Vi.ua.get=function(){return Ri.getUA()},Vi.ua.set=function(i){Ri.setUA(i);var e,t=Ri.getResult();for(e in t)Vi.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file +((i,l)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!Ti(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Pi,e):Pi,M.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Gi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Gi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return O(i)&&(r=i.length>U?Mi(i,U):i),this}]]).setUA(r),this):new V(i,e,t).getResult()}V.VERSION="2.0.4",V.BROWSER=I([v,y,G,k]),V.CPU=I([C]),V.DEVICE=I([S,x,k,W,_,e,r,t,F]),V.ENGINE=V.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=V:exports).UAParser=V:typeof define===R&&define.amd?define(function(){return V}):qi&&(i.UAParser=V);var Wi,Ni=qi&&(i.jQuery||i.Zepto);Ni&&!Ni.ua&&(Wi=new V,Ni.ua=Wi.getResult(),Ni.ua.get=function(){return Wi.getUA()},Ni.ua.set=function(i){Wi.setUA(i);var e,t=Wi.getResult();for(e in t)Ni.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs index 6c16240..03863a2 100644 --- a/dist/ua-parser.pack.mjs +++ b/dist/ua-parser.pack.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -function E(i){for(var e={},o=0;o{var o,t={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,M.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Pi(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Pi(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return A(i)&&(r=i.length>B?Ai(i,B):i),this}]]).setUA(r),this):new j(i,e,o).getResult()}j.VERSION="2.0.3",j.BROWSER=E([g,x,C,v]),j.CPU=E([y]),j.DEVICE=E([T,k,v,L,S,i,r,e,F]),j.ENGINE=j.OS=E([g,x]);export{j as UAParser}; \ No newline at end of file +function I(i){for(var e={},o=0;o{var o,t={},r=e;if(!zi(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,A.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Li(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Li(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>B?Mi(i,B):i),this}]]).setUA(r),this):new V(i,e,o).getResult()}V.VERSION="2.0.4",V.BROWSER=I([g,x,C,v]),V.CPU=I([y]),V.DEVICE=I([S,k,v,G,_,i,r,e,N]),V.ENGINE=V.OS=I([g,x]);export{V as UAParser}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 70cf439..7dd00ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.3", + "version": "2.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.3", + "version": "2.0.4", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index ba1f89d..4ca4f0b 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.3", + "version": "2.0.4", "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.d.ts b/src/enums/ua-parser-enums.d.ts index 97c760a..e8d3615 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Enums submodule of UAParser.js v2.0.3 +// Type definitions for Enums submodule of UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 803970c..174d787 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.3 +/* Enums for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 571eb70..089af95 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.3 +/* Enums for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -25,7 +25,6 @@ const Browser = Object.freeze({ BAIDU: 'Baidu Browser', BASILISK: 'Basilisk', BLAZER: 'Blazer', - BLU: 'BLU', BOLT: 'Bolt', BOWSER: 'Bowser', BRAVE: 'Brave', @@ -48,6 +47,8 @@ const Browser = Object.freeze({ DUCKDUCKGO: 'DuckDuckGo', ECOSIA: 'Ecosia', EDGE: 'Edge', + EDGE_WEBVIEW: 'Edge WebView', + EDGE_WEBVIEW2: 'Edge WebView2', EPIPHANY: 'Epiphany', FACEBOOK: 'Facebook', FALKON: 'Falkon', @@ -218,6 +219,7 @@ const Vendor = Object.freeze({ ATT: 'AT&T', BENQ: 'BenQ', BLACKBERRY: 'BlackBerry', + BLU: 'BLU', CAT: 'Cat', DELL: 'Dell', ENERGIZER: 'Energizer', @@ -236,6 +238,7 @@ const Vendor = Object.freeze({ ITEL: 'itel', JOLLA: 'Jolla', KOBO: 'Kobo', + LAVA: 'Lava', LENOVO: 'Lenovo', LG: 'LG', MEIZU: 'Meizu', @@ -256,6 +259,7 @@ const Vendor = Object.freeze({ PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', + RETROID: 'Retroid', RIM: 'RIM', ROKU: 'Roku', SAMSUNG: 'Samsung', @@ -270,6 +274,7 @@ const Vendor = Object.freeze({ TESLA: 'Tesla', ULEFONE: 'Ulefone', VIVO: 'Vivo', + VIZIO: 'Vizio', VODAFONE: 'Vodafone', XBOX: 'Xbox', XIAOMI: 'Xiaomi', @@ -307,6 +312,7 @@ const OS = Object.freeze({ AMIGA_OS: 'Amiga OS', ANDROID: 'Android', ANDROID_X86: 'Android-x86', + ARCAOS: 'ArcaOS', ARCH: 'Arch', BADA: 'Bada', BEOS: 'BeOS', @@ -337,6 +343,7 @@ const OS = Object.freeze({ IOS: 'iOS', JOLI: 'Joli', KAIOS: 'KaiOS', + KNOPPIX: 'Knoppix', KUBUNTU: 'Kubuntu', LINPUS: 'Linpus', LINSPIRE: 'Linspire', @@ -385,10 +392,13 @@ const OS = Object.freeze({ WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', + WINDOWS_CE: 'Windows CE', WINDOWS_IOT: 'Windows IoT', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', + WINDOWS_RT: 'Windows RT', XBOX: 'Xbox', + XUBUNTU: 'Xubuntu', ZENWALK: 'Zenwalk' // TODO : test! diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 3b9b63a..4ee31dd 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index d89b7ec..4cd644f 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.3 +/* Extensions for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 9770ebc..9401493 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.3 +/* Extensions for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -52,13 +52,15 @@ const Crawlers = Object.freeze({ // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot + // iAskBot - https://iask.ai // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html + // Onespot - https://www.onespot.com/identifying-traffic.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -72,6 +74,9 @@ const Crawlers = Object.freeze({ // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, + // Daum + /(daum(?:oa)?(?:-image)?)[ \/]([\w\.]+)/i, + // Facebook / Meta // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, @@ -82,6 +87,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // Qwantbot - https://help.qwant.com/bot + /(qwantbot)[-\w]*\/?([\w\.]*)/i, + // SemrushBot - http://www.semrush.com/bot.html /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, @@ -97,8 +105,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |line|yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -208,9 +216,18 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ [ - // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i - ], [NAME, VERSION, [TYPE, EMAIL]] + // Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird + // Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail + // ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop + /((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i, + + // Apple's Mail + /(mail)\/([\w\.]+) cf/i + ], [NAME, VERSION, [TYPE, EMAIL]], [ + + // Zimbra + /zdesktop\/([\w\.]+)/i + ], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]] ] }); @@ -226,8 +243,11 @@ const Fetchers = Object.freeze({ // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan + // Iframely - https://iframely.com/docs/about + // Perplexity-User - https://docs.perplexity.ai/guides/bots + // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, @@ -261,13 +281,31 @@ const Fetchers = Object.freeze({ /////////////////// const InApps = Object.freeze({ - browser : [ + browser : [[ + // Discord/Figma/Flipboard/Mattermost/Notion/Postman/Rambox/Rocket.Chat/Slack/Teams + /\b(discord|figma|mattermost|notion|postman|rambox|rocket.chat|slack|teams)\/([\w\.]+).+(electron\/|; ios)/i, + /(flipboard)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, INAPP]], [ + + // Evernote/Teams on mobile + /(evernote) win/i, + /(teams)mobile-(ios|and)/i + ], [NAME, [TYPE, INAPP]], [ + // Slack - [/(?:slack(?=.+electron|.+ios)|chatlyio)\/([\d\.]+)/i], - [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], + /chatlyio\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], [ + + // TikTok Lite + /ultralite app_version\/([\w\.]+)/i], + [VERSION, [NAME, 'TikTok Lite'], [TYPE, INAPP]], [ + + // VS Code + /\) code\/([\d\.]+).+electron\//i], + [VERSION, [NAME, 'VS Code'], [TYPE, INAPP]], [ // Yahoo! Japan - [/jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + /jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] ] }); @@ -332,8 +370,10 @@ const Libraries = Object.freeze({ browser : [ // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent [ - /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, - /(jsdom|(?<=\()java)\/([\w\.]+)/i + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i, + /(adobeair|aiohttp|jsdom)\/([\w\.]+)/i, + /(nutch)-([\w\.-]+)(\(|$)/i, + /\((java)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 4b6c808..fc53c61 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index d14667b..8f79fe8 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.3 +/* Helpers for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index e47aae4..3530e7d 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.3 +/* Helpers for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -114,6 +114,9 @@ const isAIBot = (resultOrUA) => [ // You.com 'youbot', + // Zhipu AI + 'chatglm-spider', + // Zyte 'scrapy' diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 6c7c9f8..9533b68 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.3 +// Type definitions for UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index e3373d2..b325e7b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.3', + var LIBVERSION = '2.0.4', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 00eb9bb..13d7caa 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.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.3', + var LIBVERSION = '2.0.4', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', @@ -154,11 +154,11 @@ has = function (str1, str2) { if (typeof str1 === OBJ_TYPE && str1.length > 0) { for (var i in str1) { - if (lowerize(str1[i]) == lowerize(str2)) return true; + if (lowerize(str2) == lowerize(str1[i])) return true; } return false; } - return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; + return isString(str1) ? lowerize(str2) == lowerize(str1) : false; }, isExtensions = function (obj, deep) { for (var prop in obj) { @@ -249,17 +249,25 @@ // assign given value, ignore regex match this[q[0]] = q[1]; } - } else if (q.length === 3) { - // check whether function or regex + } else if (q.length >= 3) { + // Check whether q[1] 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; + if (q.length > 3) { + this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; + } else { + // 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; + if (q.length == 3) { + // 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 if (q.length > 4) { + this[q[0]] = match ? q[3].apply(this, [match.replace(q[1], q[2])].concat(q.slice(4))) : 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; @@ -293,17 +301,17 @@ ////////////// var 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' + 'ME' : '4.90', + 'NT 3.51': '3.51', + 'NT 4.0': '4.0', + '2000' : ['5.0', '5.01'], + 'XP' : ['5.1', '5.2'], + 'Vista' : '6.0', + '7' : '6.1', + '8' : '6.2', + '8.1' : '6.3', + '10' : ['6.4', '10.0'], + 'NT' : '' }, formFactorsMap = { @@ -315,6 +323,18 @@ 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined + }, + + browserHintsMap = { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', + 'Chrome WebView': 'Android WebView', + 'Chrome Headless':'HeadlessChrome', + 'Huawei Browser': 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' }; ////////////// @@ -328,7 +348,9 @@ // Most common regardless engine /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ - /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge + /webview.+edge\/([\w\.]+)/i // Microsoft Edge + ], [VERSION, [NAME, EDGE+' WebView']], [ + /edg(?:e|ios|a)?\/([\w\.]+)/i ], [VERSION, [NAME, 'Edge']], [ // Presto based @@ -420,7 +442,6 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ - /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /(daum)apps[\/ ]([\w\.]+)/i, // Daum App @@ -428,7 +449,7 @@ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter - /(instagram|snapchat)[\/ ]([-\w\.]+)/i // Instagram/Snapchat + /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna ], [NAME, VERSION, [TYPE, INAPP]], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ @@ -443,6 +464,9 @@ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ + /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2 + ], [VERSION, [NAME, EDGE+' WebView2']], [ + / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView ], [[NAME, CHROME+' WebView'], VERSION], [ @@ -535,7 +559,7 @@ /\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)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, - /samsung[- ]((?!sm-[lr])[-\w]+)/i, + /samsung[- ]((?!sm-[lr]|browser)[-\w]+)/i, /sec-(sgh\w+)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ @@ -580,16 +604,22 @@ / ([\w ]+) miui\/v?\d/i ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + // OnePlus + /droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i, + /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ + // 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]], [ /\b(opd2(\d{3}a?))(?: bui|\))/i - ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ + + // BLU + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i // Vivo series + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ - // BLU Vivo Series - /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i - ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ // Vivo /; vivo (\w+)(?: bui|\))/i, /\b(v[12]\d{3}\w?[at])(?: bui|;)/i @@ -599,10 +629,17 @@ /\b(rmx[1-3]\d{3})(?: bui|;|\))/i ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ + // Lenovo + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ + /lenovo[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, MOBILE]], [ + // Motorola /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, - /\bmot(?:orola)?[- ](\w*)/i, - /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i + /\bmot(?:orola)?[- ]([\w\s]+)(\)| bui)/i, + /((?:moto(?! 360)[-\w\(\) ]+|xt\d{3,4}[cgkosw\+]?[-\d]*|nexus 6)(?= bui|\)))/i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ /\b(mz60\d|xoom[2 ]{0,2}) build\//i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ @@ -611,15 +648,10 @@ /((?=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|watch))(\w+)/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ - // Lenovo - /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, - /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i - ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ - // Nokia /(nokia) (t[12][01])/i ], [VENDOR, MODEL, [TYPE, TABLET]], [ @@ -630,21 +662,19 @@ // Google /(pixel (c|tablet))\b/i // Google Pixel C/Tablet ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ - /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel + // Google Pixel + /droid.+;(?: google)? (g(01[13]a|020[aem]|025[jn]|1b60|1f8f|2ybb|4s1m|576d|5nz6|8hhn|8vou|a02099|c15s|d1yq|e2ae|ec77|gh2x|kv4x|p4bc|pj41|r83y|tt9q|ur25|wvk6)|pixel[\d ]*a?( pro)?( xl)?( fold)?( \(5g\))?)( bui|\))/i ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ + /(google) (pixelbook( go)?)/i + ], [VENDOR, MODEL], [ // 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 + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= 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|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show @@ -715,7 +745,7 @@ ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ // Nothing - /droid.+; (a(?:015|06[35]|142p?))/i + /droid.+; (a(in)?(0(15|59|6[35])|142)p?)/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // Archos @@ -726,17 +756,20 @@ /; (ac[3-6]\d\w{2,8})( b|\))/i ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + // HMD + /; (n159v)/i + ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [ + // MIXED /(imo) (tab \w+)/i, // IMO - /(infinix) (x1101b?)/i // Infinix XPad + /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan + /; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ @@ -768,6 +801,7 @@ ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(vizio)(?: |.+model\/)(\w+-\w+)/i, // Vizio /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i @@ -799,26 +833,27 @@ ], [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]], [ + ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [ // SmartTV from Unidentified Vendors /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i ], [MODEL, [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:|large screen[\w ]+safari)\b/i ], [[TYPE, SMARTTV]], [ /////////////////// // CONSOLES /////////////////// - /(ouya)/i, // Ouya - /(nintendo) (\w+)/i // Nintendo - ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ - /droid.+; (shield)( bui|\))/i // Nvidia Portable - ], [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]], [ + /(ouya)/i, // Ouya + /(nintendo) (\w+)/i, // Nintendo + /(retroid) (pocket ([^\)]+))/i // Retroid Pocket + ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /////////////////// // WEARABLES @@ -854,6 +889,8 @@ ], [VENDOR, MODEL, [TYPE, XR]], [ /(quest( \d| pro)?s?).+vr/i // Meta Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ + /mobile vr; rv.+firefox/i // Unidentifiable VR device using Firefox Reality / Wolvic + ], [[TYPE, XR]], [ /////////////////// // EMBEDDED @@ -865,7 +902,7 @@ ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ /(homepod).+mac os/i // Apple HomePod ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ - /windows iot/i + /windows iot/i // Unidentifiable embedded device using Windows IoT ], [[TYPE, EMBEDDED]], [ //////////////////// @@ -911,14 +948,17 @@ os : [[ // Windows - /microsoft (windows) (vista|xp)/i // Windows (iTunes) + /(windows nt) (6\.[23]); arm/i // Windows RT + ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ + /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone + // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|server 20\d\d( r2)?|vista|xp)/i + ], [NAME, VERSION], [ + /windows nt ?([\d\.\)]*)(?!.+xbox)/i, + /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i + ], [[VERSION, /(;|\))/g, '', strMapper, windowsVersionMap], [NAME, WINDOWS]], [ + /(windows ce)\/?([\d\.]*)/i // Windows CE ], [NAME, VERSION], [ - /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone - ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /windows nt 6\.2; (arm)/i, // Windows RT - /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i, - /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i - ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS @@ -926,7 +966,7 @@ /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, - /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS + /(macintosh|mac_powerpc\b)(?!.+(haiku|morphos))/i // Mac OS ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ // Google Chromecast @@ -942,12 +982,13 @@ ], [VERSION, [NAME, CHROMECAST]], [ // Mobile OSes - /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS + /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86 ], [VERSION, NAME], [ /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen - /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ @@ -955,9 +996,12 @@ ], [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 + /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS + /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i ], [VERSION, [NAME, 'webOS']], [ + /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i + // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine + ], [[VERSION, strMapper, {'25':'120','24':'108','23':'94','22':'87','6':'79','5':'68','4':'53','3':'38','2':'538','1':'537','*':'TV'}], [NAME, 'webOS']], [ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS ], [VERSION, [NAME, 'watchOS']], [ @@ -977,20 +1021,20 @@ // 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)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux + /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint + /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire|knoppix)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire/Knoppix + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris + /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX + /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS /(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 + /(haiku) ?(r\d)?/i // Haiku ], [NAME, VERSION], [ - /(sunos) ?([\w\.\d]*)/i // Solaris + /(sunos) ?([\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|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS + /\b(beos|os\/2|amigaos|openvms|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] @@ -1224,21 +1268,20 @@ for (var i in brands) { var brandName = brands[i].brand || brands[i], brandVersion = brands[i].version; - if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) { - brandName = strMapper(brandName, { - 'Chrome' : 'Google Chrome', - 'Edge' : 'Microsoft Edge', - 'Chrome WebView' : 'Android WebView', - 'Chrome Headless' : 'HeadlessChrome', - 'Huawei Browser' : 'HuaweiBrowser', - 'MIUI Browser' : 'Miui Browser', - 'Opera Mobi' : 'OperaMobile', - 'Yandex' : 'YaBrowser' - }); - this.set(NAME, brandName) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - prevName = brandName; + if (this.itemType == UA_BROWSER && + !/not.a.brand/i.test(brandName) && + (!prevName || + (/Chrom/.test(prevName) && brandName != CHROMIUM) || + (prevName == EDGE && /WebView2/.test(brandName)) + )) { + brandName = strMapper(brandName, browserHintsMap); + prevName = this.get(NAME); + if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) { + this.set(NAME, brandName) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + prevName = brandName; } if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { this.set(VERSION, brandVersion);