@naturalcycles/js-lib 15.85.0 → 15.86.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ua/ua.js ADDED
@@ -0,0 +1,1396 @@
1
+ // Vendored & modernized from https://github.com/mcollina/my-ua-parser (v2.0.4, commit 0ff5baa),
2
+ // which is an MIT fork of https://github.com/faisalman/ua-parser-js
3
+ // Copyright © 2024 Matteo Collina <hello@matteocollina.com>
4
+ // Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
5
+ // MIT license.
6
+ //
7
+ // Regex data refreshed from ua-parser-js v1.0.41 (`1.0.x` branch, 2025-08-19), which stayed
8
+ // MIT-licensed. Nothing is taken from ua-parser-js v2.x, which is AGPL-3.0-licensed.
9
+ //
10
+ // Detects Browser, Engine, OS, CPU and Device type/model from a User-Agent string.
11
+ // Supports browser & Node.js environments.
12
+ /// <reference lib="dom" preserve="true" />
13
+ /**
14
+ * Popular OS names as produced by the parser, for convenient comparisons
15
+ * (`os.name === UAOSName.iOS`) without magic strings.
16
+ *
17
+ * Not exhaustive - the parser emits many more names; compare against a string for the rest.
18
+ */
19
+ export var UAOSName;
20
+ (function (UAOSName) {
21
+ UAOSName["iOS"] = "iOS";
22
+ UAOSName["Android"] = "Android";
23
+ UAOSName["MacOS"] = "Mac OS";
24
+ UAOSName["Windows"] = "Windows";
25
+ UAOSName["Linux"] = "Linux";
26
+ UAOSName["ChromiumOS"] = "Chromium OS";
27
+ UAOSName["WatchOS"] = "watchOS";
28
+ })(UAOSName || (UAOSName = {}));
29
+ /**
30
+ * Popular browser names as produced by the parser, for convenient comparisons
31
+ * (`browser.name === UABrowserName.Safari`) without magic strings.
32
+ *
33
+ * Not exhaustive - the parser emits many more names; compare against a string for the rest.
34
+ */
35
+ export var UABrowserName;
36
+ (function (UABrowserName) {
37
+ UABrowserName["Chrome"] = "Chrome";
38
+ UABrowserName["ChromeWebView"] = "Chrome WebView";
39
+ UABrowserName["ChromeHeadless"] = "Chrome Headless";
40
+ UABrowserName["Safari"] = "Safari";
41
+ UABrowserName["MobileSafari"] = "Mobile Safari";
42
+ UABrowserName["Firefox"] = "Firefox";
43
+ UABrowserName["Edge"] = "Edge";
44
+ UABrowserName["Opera"] = "Opera";
45
+ UABrowserName["SamsungInternet"] = "Samsung Internet";
46
+ UABrowserName["Facebook"] = "Facebook";
47
+ UABrowserName["Instagram"] = "Instagram";
48
+ /**
49
+ * Bare WebKit UA without a Safari token, typically an iOS in-app webview (e.g. WKWebView).
50
+ */
51
+ UABrowserName["WebKit"] = "WebKit";
52
+ })(UABrowserName || (UABrowserName = {}));
53
+ /**
54
+ * Parses the given User-Agent string into browser, engine, os, device and cpu information.
55
+ *
56
+ * When `ua` is not provided and running in a browser - `navigator.userAgent` is used
57
+ * (with client-hints-based refinements, e.g. Brave and iPadOS detection).
58
+ */
59
+ export function parseUserAgent(userAgent, extensions) {
60
+ return new UAParser(userAgent, extensions).getResult();
61
+ }
62
+ export function uaParser(userAgent, extensions) {
63
+ return new UAParser(userAgent, extensions);
64
+ }
65
+ /**
66
+ * User-Agent parser.
67
+ *
68
+ * Compared to `parseUserAgent()`, the class allows parsing only the needed parts
69
+ * (e.g `getBrowser()` alone is much cheaper than `getResult()`),
70
+ * and reusing the compiled extensions across multiple `setUA()` calls.
71
+ */
72
+ export class UAParser {
73
+ constructor(ua, extensions) {
74
+ this.nav = typeof globalThis.window !== 'undefined' ? globalThis.navigator : undefined;
75
+ this.rgxMap = extensions ? extend(regexes, extensions) : regexes;
76
+ const initialUa = ua || this.nav?.userAgent || '';
77
+ // Whether we're parsing the environment's own UA - only then the
78
+ // navigator-based refinements (Brave, client hints, iPadOS) apply
79
+ this.isSelfNav = !!this.nav && this.nav.userAgent === initialUa;
80
+ this.setUA(initialUa);
81
+ }
82
+ ua = '';
83
+ nav;
84
+ rgxMap;
85
+ isSelfNav;
86
+ // Lazily cached results shared by the is* helpers (so multiple checks cost one parse),
87
+ // reset by setUA(). The get* methods deliberately stay uncached and return fresh objects.
88
+ cachedBrowser;
89
+ cachedOs;
90
+ cachedDevice;
91
+ getBrowser() {
92
+ const browser = { name: undefined, version: undefined, major: undefined };
93
+ rgxMapper(browser, this.ua, this.rgxMap.browser);
94
+ browser.major = majorize(browser.version);
95
+ // Brave-specific detection
96
+ if (this.isSelfNav && typeof this.nav?.brave?.isBrave === 'function') {
97
+ browser.name = 'Brave';
98
+ }
99
+ return browser;
100
+ }
101
+ getCPU() {
102
+ const cpu = { architecture: undefined };
103
+ rgxMapper(cpu, this.ua, this.rgxMap.cpu);
104
+ return cpu;
105
+ }
106
+ getDevice() {
107
+ const device = { vendor: undefined, model: undefined, type: undefined };
108
+ rgxMapper(device, this.ua, this.rgxMap.device);
109
+ if (this.isSelfNav && !device.type && this.nav?.userAgentData?.mobile) {
110
+ device.type = MOBILE;
111
+ }
112
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
113
+ if (this.isSelfNav &&
114
+ device.model === 'Macintosh' &&
115
+ typeof this.nav?.standalone !== 'undefined' &&
116
+ this.nav.maxTouchPoints > 2) {
117
+ device.model = 'iPad';
118
+ device.type = TABLET;
119
+ }
120
+ return device;
121
+ }
122
+ getEngine() {
123
+ const engine = { name: undefined, version: undefined };
124
+ rgxMapper(engine, this.ua, this.rgxMap.engine);
125
+ return engine;
126
+ }
127
+ getOS() {
128
+ const os = { name: undefined, version: undefined };
129
+ rgxMapper(os, this.ua, this.rgxMap.os);
130
+ const platform = this.nav?.userAgentData?.platform;
131
+ if (this.isSelfNav && !os.name && platform && platform !== 'Unknown') {
132
+ os.name = platform.replace(/chrome os/i, CHROMIUM_OS).replace(/macos/i, MAC_OS); // backward compatibility
133
+ }
134
+ return os;
135
+ }
136
+ getResult() {
137
+ return {
138
+ ua: this.getUA(),
139
+ browser: this.getBrowser(),
140
+ engine: this.getEngine(),
141
+ os: this.getOS(),
142
+ device: this.getDevice(),
143
+ cpu: this.getCPU(),
144
+ };
145
+ }
146
+ getUA() {
147
+ return this.ua;
148
+ }
149
+ setUA(ua) {
150
+ ua ||= '';
151
+ this.ua = ua.length > UA_MAX_LENGTH ? ua.replace(/^\s\s*/, '').slice(0, UA_MAX_LENGTH) : ua;
152
+ this.cachedBrowser = this.cachedOs = this.cachedDevice = undefined;
153
+ return this;
154
+ }
155
+ /**
156
+ * True if the OS is iOS.
157
+ * Note: desktop-mode iPads send a Mac-like UA and are not detected as iOS
158
+ * (except when parsing the browser's own UA, where client-hints refinements apply).
159
+ */
160
+ isIos() {
161
+ return this.os().name === UAOSName.iOS;
162
+ }
163
+ isAndroid() {
164
+ return this.os().name === UAOSName.Android;
165
+ }
166
+ isMacOs() {
167
+ return this.os().name === UAOSName.MacOS;
168
+ }
169
+ isWindows() {
170
+ return this.os().name === UAOSName.Windows;
171
+ }
172
+ /**
173
+ * True for 'Chrome', 'Chrome WebView' and 'Chrome Headless' (not for Chromium).
174
+ */
175
+ isChrome() {
176
+ return !!this.browser().name?.startsWith(UABrowserName.Chrome);
177
+ }
178
+ /**
179
+ * True for 'Safari' and 'Mobile Safari' (not for bare-WebKit iOS in-app webviews).
180
+ */
181
+ isSafari() {
182
+ const { name } = this.browser();
183
+ return name === UABrowserName.Safari || name === UABrowserName.MobileSafari;
184
+ }
185
+ isFirefox() {
186
+ return this.browser().name === UABrowserName.Firefox;
187
+ }
188
+ isEdge() {
189
+ return this.browser().name === UABrowserName.Edge;
190
+ }
191
+ /**
192
+ * True if the device type is 'mobile' (phones; tablets are 'tablet' - see isTablet).
193
+ */
194
+ isMobile() {
195
+ return this.device().type === MOBILE;
196
+ }
197
+ isTablet() {
198
+ return this.device().type === TABLET;
199
+ }
200
+ browser() {
201
+ return (this.cachedBrowser ??= this.getBrowser());
202
+ }
203
+ os() {
204
+ return (this.cachedOs ??= this.getOS());
205
+ }
206
+ device() {
207
+ return (this.cachedDevice ??= this.getDevice());
208
+ }
209
+ }
210
+ ///////////
211
+ // Helpers
212
+ //////////
213
+ function extend(defaults, extensions) {
214
+ return {
215
+ browser: mergeMapEntries(defaults.browser, extensions.browser),
216
+ cpu: mergeMapEntries(defaults.cpu, extensions.cpu),
217
+ device: mergeMapEntries(defaults.device, extensions.device),
218
+ engine: mergeMapEntries(defaults.engine, extensions.engine),
219
+ os: mergeMapEntries(defaults.os, extensions.os),
220
+ };
221
+ }
222
+ function mergeMapEntries(defaults, extension) {
223
+ // extension must be a valid alternating [regexes, actions, ...] sequence, otherwise ignored
224
+ return extension?.length && extension.length % 2 === 0 ? extension.concat(defaults) : defaults;
225
+ }
226
+ function rgxMapper(target, ua, arrays) {
227
+ const t = target;
228
+ let matches = null;
229
+ // loop through all regexes maps
230
+ for (let i = 0; i < arrays.length && !matches; i += 2) {
231
+ const regexList = arrays[i]; // even sequence (0,2,4,..)
232
+ const actions = arrays[i + 1]; // odd sequence (1,3,5,..)
233
+ let k = 0;
234
+ // try matching uastring with regexes
235
+ for (let j = 0; j < regexList.length && !matches; j++) {
236
+ const regex = regexList[j];
237
+ if (!regex)
238
+ break;
239
+ matches = regex.exec(ua);
240
+ if (matches) {
241
+ for (const q of actions) {
242
+ const match = matches[++k];
243
+ if (typeof q === 'string') {
244
+ // assign the raw match
245
+ t[q] = match || undefined;
246
+ }
247
+ else if (q.length === 2) {
248
+ if (typeof q[1] === 'function') {
249
+ // assign modified match
250
+ t[q[0]] = match === undefined ? undefined : q[1](match);
251
+ }
252
+ else {
253
+ // assign given value, ignore regex match
254
+ t[q[0]] = q[1];
255
+ }
256
+ }
257
+ else if (q.length === 3) {
258
+ if (typeof q[1] === 'function') {
259
+ // call function (usually string mapper)
260
+ // typeof check on q[1] doesn't discriminate the tuple union, hence the cast
261
+ const [prop, fn, map] = q;
262
+ t[prop] = match ? fn(match, map) : undefined;
263
+ }
264
+ else {
265
+ // sanitize match using given regex
266
+ const [prop, rgx, replacement] = q;
267
+ t[prop] = match ? match.replace(rgx, replacement) : undefined;
268
+ }
269
+ }
270
+ else if (q.length === 4) {
271
+ t[q[0]] = match ? q[3](match.replace(q[1], q[2])) : undefined;
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
277
+ }
278
+ function strMapper(str, map) {
279
+ for (const key of Object.keys(map)) {
280
+ const value = map[key];
281
+ // check if current value is array
282
+ if (Array.isArray(value)) {
283
+ for (const v of value) {
284
+ if (has(v, str)) {
285
+ return key === UNKNOWN ? undefined : key;
286
+ }
287
+ }
288
+ }
289
+ else if (has(value, str)) {
290
+ return key === UNKNOWN ? undefined : key;
291
+ }
292
+ }
293
+ // '*' is a catch-all fallback value (always a string in the built-in maps)
294
+ const fallback = map['*'];
295
+ return typeof fallback === 'string' ? fallback : str;
296
+ }
297
+ function majorize(version) {
298
+ return typeof version === 'string' ? version.replaceAll(/[^\d\.]/g, '').split('.')[0] : undefined;
299
+ }
300
+ function has(str1, str2) {
301
+ return lowerize(str2).includes(lowerize(str1));
302
+ }
303
+ function lowerize(str) {
304
+ return str.toLowerCase();
305
+ }
306
+ function trimStr(str) {
307
+ return str.replace(/^\s\s*/, '');
308
+ }
309
+ //////////////
310
+ // Constants
311
+ /////////////
312
+ const UNKNOWN = '?';
313
+ const UA_MAX_LENGTH = 500;
314
+ const MODEL = 'model';
315
+ const NAME = 'name';
316
+ const TYPE = 'type';
317
+ const VENDOR = 'vendor';
318
+ const VERSION = 'version';
319
+ const ARCHITECTURE = 'architecture';
320
+ const CONSOLE = 'console';
321
+ const MOBILE = 'mobile';
322
+ const TABLET = 'tablet';
323
+ const SMARTTV = 'smarttv';
324
+ const WEARABLE = 'wearable';
325
+ const EMBEDDED = 'embedded';
326
+ const AMAZON = 'Amazon';
327
+ const APPLE = 'Apple';
328
+ const ASUS = 'ASUS';
329
+ const BLACKBERRY = 'BlackBerry';
330
+ const BROWSER = 'Browser';
331
+ const SUFFIX_BROWSER = ' Browser';
332
+ const CHROME = UABrowserName.Chrome;
333
+ const EDGE = UABrowserName.Edge;
334
+ const FIREFOX = UABrowserName.Firefox;
335
+ const GOOGLE = 'Google';
336
+ const HONOR = 'Honor';
337
+ const HUAWEI = 'Huawei';
338
+ const LENOVO = 'Lenovo';
339
+ const LG = 'LG';
340
+ const MICROSOFT = 'Microsoft';
341
+ const MOTOROLA = 'Motorola';
342
+ const NVIDIA = 'Nvidia';
343
+ const ONEPLUS = 'OnePlus';
344
+ const OPERA = UABrowserName.Opera;
345
+ const OPPO = 'OPPO';
346
+ const SAMSUNG = 'Samsung';
347
+ const SHARP = 'Sharp';
348
+ const SONY = 'Sony';
349
+ const XIAOMI = 'Xiaomi';
350
+ const ZEBRA = 'Zebra';
351
+ const FACEBOOK = UABrowserName.Facebook;
352
+ const CHROMIUM_OS = UAOSName.ChromiumOS;
353
+ const MAC_OS = UAOSName.MacOS;
354
+ ///////////////
355
+ // String maps
356
+ //////////////
357
+ // Safari < 3.0
358
+ const oldSafariMap = {
359
+ '1.0': '/8',
360
+ '1.2': '/1',
361
+ '1.3': '/3',
362
+ '2.0': '/412',
363
+ '2.0.2': '/416',
364
+ '2.0.3': '/417',
365
+ '2.0.4': '/419',
366
+ '?': '/',
367
+ };
368
+ const windowsVersionMap = {
369
+ ME: '4.90',
370
+ 'NT 3.11': 'NT3.51',
371
+ 'NT 4.0': 'NT4.0',
372
+ 2000: 'NT 5.0',
373
+ XP: ['NT 5.1', 'NT 5.2'],
374
+ Vista: 'NT 6.0',
375
+ 7: 'NT 6.1',
376
+ 8: 'NT 6.2',
377
+ 8.1: 'NT 6.3',
378
+ 10: ['NT 6.4', 'NT 10.0'],
379
+ RT: 'ARM',
380
+ };
381
+ //////////////
382
+ // Regex map
383
+ /////////////
384
+ // The regexes below are preserved verbatim from upstream ua-parser-js 1.0.41 (MIT) -
385
+ // do not "fix" escapes or simplify them, to keep them diffable against upstream.
386
+ const regexes = {
387
+ browser: [
388
+ [
389
+ /\b(?:crmo|crios)\/([\w\.]+)/i, // Chrome for Android/iOS
390
+ ],
391
+ [VERSION, [NAME, CHROME]],
392
+ [
393
+ /edg(?:e|ios|a)?\/([\w\.]+)/i, // Microsoft Edge
394
+ ],
395
+ [VERSION, [NAME, EDGE]],
396
+ [
397
+ // Presto based
398
+ /(opera mini)\/([-\w\.]+)/i, // Opera Mini
399
+ /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet
400
+ /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i, // Opera
401
+ ],
402
+ [NAME, VERSION],
403
+ [
404
+ /opios[\/ ]+([\w\.]+)/i, // Opera mini on iphone >= 8.0
405
+ ],
406
+ [VERSION, [NAME, OPERA + ' Mini']],
407
+ [
408
+ /\bop(?:rg)?x\/([\w\.]+)/i, // Opera GX
409
+ ],
410
+ [VERSION, [NAME, OPERA + ' GX']],
411
+ [
412
+ /\bopr\/([\w\.]+)/i, // Opera Webkit
413
+ ],
414
+ [VERSION, [NAME, OPERA]],
415
+ [
416
+ // Mixed
417
+ /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i, // Baidu
418
+ ],
419
+ [VERSION, [NAME, 'Baidu']],
420
+ [
421
+ /\b(?:mxbrowser|mxios|myie2)\/?([-\w\.]*)\b/i, // Maxthon
422
+ ],
423
+ [VERSION, [NAME, 'Maxthon']],
424
+ [
425
+ /(kindle)\/([\w\.]+)/i, // Kindle
426
+ /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i,
427
+ // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir
428
+ // Trident based
429
+ /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet
430
+ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
431
+ // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
432
+ /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon)\/([-\w\.]+)/i,
433
+ // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
434
+ /(heytap|ovi|115)browser\/([\d\.]+)/i, // HeyTap/Ovi/115
435
+ /(weibo)__([\d\.]+)/i, // Weibo
436
+ ],
437
+ [NAME, VERSION],
438
+ [
439
+ /quark(?:pc)?\/([-\w\.]+)/i, // Quark
440
+ ],
441
+ [VERSION, [NAME, 'Quark']],
442
+ [
443
+ /\bddg\/([\w\.]+)/i, // DuckDuckGo
444
+ ],
445
+ [VERSION, [NAME, 'DuckDuckGo']],
446
+ [
447
+ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i, // UCBrowser
448
+ ],
449
+ [VERSION, [NAME, 'UC' + BROWSER]],
450
+ [
451
+ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
452
+ /\bqbcore\/([\w\.]+).+microm/i,
453
+ /micromessenger\/([\w\.]+)/i, // WeChat
454
+ ],
455
+ [VERSION, [NAME, 'WeChat']],
456
+ [
457
+ /konqueror\/([\w\.]+)/i, // Konqueror
458
+ ],
459
+ [VERSION, [NAME, 'Konqueror']],
460
+ [
461
+ /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i, // IE11
462
+ ],
463
+ [VERSION, [NAME, 'IE']],
464
+ [
465
+ /ya(?:search)?browser\/([\w\.]+)/i, // Yandex
466
+ ],
467
+ [VERSION, [NAME, 'Yandex']],
468
+ [
469
+ /slbrowser\/([\w\.]+)/i, // Smart Lenovo Browser
470
+ ],
471
+ [VERSION, [NAME, 'Smart Lenovo ' + BROWSER]],
472
+ [
473
+ /(avast|avg)\/([\w\.]+)/i, // Avast/AVG Secure Browser
474
+ ],
475
+ [[NAME, /(.+)/, '$1 Secure ' + BROWSER], VERSION],
476
+ [
477
+ /\bfocus\/([\w\.]+)/i, // Firefox Focus
478
+ ],
479
+ [VERSION, [NAME, FIREFOX + ' Focus']],
480
+ [
481
+ /\bopt\/([\w\.]+)/i, // Opera Touch
482
+ ],
483
+ [VERSION, [NAME, OPERA + ' Touch']],
484
+ [
485
+ /coc_coc\w+\/([\w\.]+)/i, // Coc Coc Browser
486
+ ],
487
+ [VERSION, [NAME, 'Coc Coc']],
488
+ [
489
+ /dolfin\/([\w\.]+)/i, // Dolphin
490
+ ],
491
+ [VERSION, [NAME, 'Dolphin']],
492
+ [
493
+ /coast\/([\w\.]+)/i, // Opera Coast
494
+ ],
495
+ [VERSION, [NAME, OPERA + ' Coast']],
496
+ [
497
+ /miuibrowser\/([\w\.]+)/i, // MIUI Browser
498
+ ],
499
+ [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]],
500
+ [
501
+ /fxios\/([\w\.-]+)/i, // Firefox for iOS
502
+ ],
503
+ [VERSION, [NAME, FIREFOX]],
504
+ [
505
+ /\bqihoobrowser\/?([\w\.]*)/i, // 360
506
+ ],
507
+ [VERSION, [NAME, '360']],
508
+ [
509
+ /\b(qq)\/([\w\.]+)/i, // QQ
510
+ ],
511
+ [[NAME, /(.+)/, '$1Browser'], VERSION],
512
+ [/(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i],
513
+ [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser
514
+ [
515
+ /samsungbrowser\/([\w\.]+)/i, // Samsung Internet
516
+ ],
517
+ [VERSION, [NAME, UABrowserName.SamsungInternet]],
518
+ [
519
+ /metasr[\/ ]?([\d\.]+)/i, // Sogou Explorer
520
+ ],
521
+ [VERSION, [NAME, 'Sogou Explorer']],
522
+ [
523
+ /(sogou)mo\w+\/([\d\.]+)/i, // Sogou Mobile
524
+ ],
525
+ [[NAME, 'Sogou Mobile'], VERSION],
526
+ [
527
+ /(electron)\/([\w\.]+) safari/i, // Electron-based App
528
+ /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
529
+ /m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i, // QQ/2345
530
+ ],
531
+ [NAME, VERSION],
532
+ [
533
+ /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq
534
+ /\[(linkedin)app\]/i, // LinkedIn App for iOS & Android
535
+ ],
536
+ [NAME],
537
+ [
538
+ /ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron
539
+ /ome\/([\w\.]+).+qihu (360)[es]e/i, // 360
540
+ ],
541
+ [VERSION, NAME],
542
+ [
543
+ // WebView
544
+ /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i, // Facebook App for iOS & Android
545
+ ],
546
+ [[NAME, FACEBOOK], VERSION],
547
+ [
548
+ /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android
549
+ /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App
550
+ /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
551
+ /(daum)apps[\/ ]([\w\.]+)/i, // Daum App
552
+ /safari (line)\/([\w\.]+)/i, // Line App for iOS
553
+ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
554
+ /(alipay)client\/([\w\.]+)/i, // Alipay
555
+ /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
556
+ /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i, // Chromium/Instagram/Snapchat
557
+ ],
558
+ [NAME, VERSION],
559
+ [
560
+ /\bgsa\/([\w\.]+) .*safari\//i, // Google Search Appliance on iOS
561
+ ],
562
+ [VERSION, [NAME, 'GSA']],
563
+ [
564
+ /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i, // TikTok
565
+ ],
566
+ [VERSION, [NAME, 'TikTok']],
567
+ [
568
+ /headlesschrome(?:\/([\w\.]+)| )/i, // Chrome Headless
569
+ ],
570
+ [VERSION, [NAME, UABrowserName.ChromeHeadless]],
571
+ [
572
+ / wv\).+(chrome)\/([\w\.]+)/i, // Chrome WebView
573
+ ],
574
+ [[NAME, UABrowserName.ChromeWebView], VERSION],
575
+ [
576
+ /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i, // Android Browser
577
+ ],
578
+ [VERSION, [NAME, 'Android ' + BROWSER]],
579
+ [
580
+ /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i, // Chrome/OmniWeb/Arora/Tizen/Nokia
581
+ ],
582
+ [NAME, VERSION],
583
+ [
584
+ /version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i, // Mobile Safari
585
+ ],
586
+ [VERSION, [NAME, UABrowserName.MobileSafari]],
587
+ [
588
+ /version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i, // Safari & Safari Mobile
589
+ ],
590
+ [VERSION, NAME],
591
+ [
592
+ /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i, // Safari < 3.0
593
+ ],
594
+ [NAME, [VERSION, strMapper, oldSafariMap]],
595
+ [/(webkit|khtml)\/([\w\.]+)/i],
596
+ [NAME, VERSION],
597
+ [
598
+ // Gecko based
599
+ /(navigator|netscape\d?)\/([-\w\.]+)/i, // Netscape
600
+ ],
601
+ [[NAME, 'Netscape'], VERSION],
602
+ [
603
+ /(wolvic|librewolf)\/([\w\.]+)/i, // Wolvic/LibreWolf
604
+ ],
605
+ [NAME, VERSION],
606
+ [
607
+ /mobile vr; rv:([\w\.]+)\).+firefox/i, // Firefox Reality
608
+ ],
609
+ [VERSION, [NAME, FIREFOX + ' Reality']],
610
+ [
611
+ /ekiohf.+(flow)\/([\w\.]+)/i, // Flow
612
+ /(swiftfox)/i, // Swiftfox
613
+ /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
614
+ // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
615
+ /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
616
+ // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
617
+ /(firefox)\/([\w\.]+)/i, // Other Firefox-based
618
+ /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
619
+ ],
620
+ [NAME, VERSION],
621
+ [
622
+ // Other
623
+ /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
624
+ // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser/Ladybird
625
+ /\b(links) \(([\w\.]+)/i, // Links
626
+ ],
627
+ [NAME, [VERSION, /_/g, '.']],
628
+ [
629
+ /(cobalt)\/([\w\.]+)/i, // Cobalt
630
+ ],
631
+ [NAME, [VERSION, /master.|lts./, '']],
632
+ ],
633
+ cpu: [
634
+ [
635
+ /\b((amd|x|x86[-_]?|wow|win)64)\b/i, // AMD64 (x64)
636
+ ],
637
+ [[ARCHITECTURE, 'amd64']],
638
+ [
639
+ /(ia32(?=;))/i, // IA32 (quicktime)
640
+ /\b((i[346]|x)86)(pc)?\b/i, // IA32 (x86)
641
+ ],
642
+ [[ARCHITECTURE, 'ia32']],
643
+ [
644
+ /\b(aarch64|arm(v?[89]e?l?|_?64))\b/i, // ARM64
645
+ ],
646
+ [[ARCHITECTURE, 'arm64']],
647
+ [
648
+ /\b(arm(v[67])?ht?n?[fl]p?)\b/i, // ARMHF
649
+ ],
650
+ [[ARCHITECTURE, 'armhf']],
651
+ [
652
+ // PocketPC mistakenly identified as PowerPC
653
+ /( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i,
654
+ ],
655
+ [[ARCHITECTURE, 'arm']],
656
+ [
657
+ /((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC
658
+ ],
659
+ [[ARCHITECTURE, /ower/, '', lowerize]],
660
+ [
661
+ / sun4\w[;\)]/i, // SPARC
662
+ ],
663
+ [[ARCHITECTURE, 'sparc']],
664
+ [
665
+ /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i,
666
+ // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
667
+ ],
668
+ [[ARCHITECTURE, lowerize]],
669
+ ],
670
+ device: [
671
+ [
672
+ //////////////////////////
673
+ // MOBILES & TABLETS
674
+ /////////////////////////
675
+ // Samsung
676
+ /\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,
677
+ ],
678
+ [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]],
679
+ [
680
+ /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,
681
+ /samsung[- ]((?!sm-[lr])[-\w]+)/i,
682
+ /sec-(sgh\w+)/i,
683
+ ],
684
+ [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]],
685
+ [
686
+ // Apple
687
+ /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i, // iPod/iPhone
688
+ ],
689
+ [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]],
690
+ [
691
+ /\((ipad);[-\w\),; ]+apple/i, // iPad
692
+ /applecoremedia\/[\w\.]+ \((ipad)/i,
693
+ /\b(ipad)\d\d?,\d\d?[;\]].+ios/i,
694
+ ],
695
+ [MODEL, [VENDOR, APPLE], [TYPE, TABLET]],
696
+ [/(macintosh);/i],
697
+ [MODEL, [VENDOR, APPLE]],
698
+ [
699
+ // Sharp
700
+ /\b(sh-?[altvz]?\d\d[a-ekm]?)/i,
701
+ ],
702
+ [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]],
703
+ [
704
+ // Honor
705
+ /\b((?:brt|eln|hey2?|gdi|jdn)-a?[lnw]09|(?:ag[rm]3?|jdn2|kob2)-a?[lw]0[09]hn)(?: bui|\)|;)/i,
706
+ ],
707
+ [MODEL, [VENDOR, HONOR], [TYPE, TABLET]],
708
+ [/honor([-\w ]+)[;\)]/i],
709
+ [MODEL, [VENDOR, HONOR], [TYPE, MOBILE]],
710
+ [
711
+ // Huawei
712
+ /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i,
713
+ ],
714
+ [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]],
715
+ [
716
+ /(?:huawei)([-\w ]+)[;\)]/i,
717
+ /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i,
718
+ ],
719
+ [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]],
720
+ [
721
+ // Xiaomi
722
+ /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i,
723
+ /\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i, // Mi Pad tablets
724
+ ],
725
+ [
726
+ [MODEL, /_/g, ' '],
727
+ [VENDOR, XIAOMI],
728
+ [TYPE, TABLET],
729
+ ],
730
+ [
731
+ /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
732
+ /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
733
+ /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
734
+ /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
735
+ /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
736
+ /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi
737
+ / ([\w ]+) miui\/v?\d/i,
738
+ ],
739
+ [
740
+ [MODEL, /_/g, ' '],
741
+ [VENDOR, XIAOMI],
742
+ [TYPE, MOBILE],
743
+ ],
744
+ [
745
+ // OPPO
746
+ /; (\w+) bui.+ oppo/i,
747
+ /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i,
748
+ ],
749
+ [MODEL, [VENDOR, OPPO], [TYPE, MOBILE]],
750
+ [/\b(opd2(\d{3}a?))(?: bui|\))/i],
751
+ [MODEL, [VENDOR, strMapper, { OnePlus: ['304', '403', '203'], '*': OPPO }], [TYPE, TABLET]],
752
+ [
753
+ // Vivo
754
+ /vivo (\w+)(?: bui|\))/i,
755
+ /\b(v[12]\d{3}\w?[at])(?: bui|;)/i,
756
+ ],
757
+ [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]],
758
+ [
759
+ // Realme
760
+ /\b(rmx[1-3]\d{3})(?: bui|;|\))/i,
761
+ ],
762
+ [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]],
763
+ [
764
+ // Motorola
765
+ /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
766
+ /\bmot(?:orola)?[- ](\w*)/i,
767
+ /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i,
768
+ ],
769
+ [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]],
770
+ [/\b(mz60\d|xoom[2 ]{0,2}) build\//i],
771
+ [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]],
772
+ [
773
+ // LG
774
+ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i,
775
+ ],
776
+ [MODEL, [VENDOR, LG], [TYPE, TABLET]],
777
+ [
778
+ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
779
+ /\blg[-e;\/ ]+((?!browser|netcast|android tv|watch)\w+)/i,
780
+ /\blg-?([\d\w]+) bui/i,
781
+ ],
782
+ [MODEL, [VENDOR, LG], [TYPE, MOBILE]],
783
+ [
784
+ // Lenovo
785
+ /(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,
786
+ /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i,
787
+ ],
788
+ [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]],
789
+ [
790
+ // Nokia
791
+ /(nokia) (t[12][01])/i,
792
+ ],
793
+ [VENDOR, MODEL, [TYPE, TABLET]],
794
+ [/(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i, /nokia[-_ ]?(([-\w\. ]*))/i],
795
+ [
796
+ [MODEL, /_/g, ' '],
797
+ [TYPE, MOBILE],
798
+ [VENDOR, 'Nokia'],
799
+ ],
800
+ [
801
+ // Google
802
+ /(pixel (c|tablet))\b/i, // Google Pixel C/Tablet
803
+ ],
804
+ [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]],
805
+ [
806
+ /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i, // Google Pixel
807
+ ],
808
+ [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]],
809
+ [
810
+ // Sony
811
+ /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,
812
+ ],
813
+ [MODEL, [VENDOR, SONY], [TYPE, MOBILE]],
814
+ [/sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i],
815
+ [
816
+ [MODEL, 'Xperia Tablet'],
817
+ [VENDOR, SONY],
818
+ [TYPE, TABLET],
819
+ ],
820
+ [
821
+ // OnePlus
822
+ / (kb2005|in20[12]5|be20[12][59])\b/i,
823
+ /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i,
824
+ ],
825
+ [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]],
826
+ [
827
+ // Amazon
828
+ /(alexa)webm/i,
829
+ /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show
830
+ /(kf[a-z]+)( bui|\)).+silk\//i, // Kindle Fire HD
831
+ ],
832
+ [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]],
833
+ [
834
+ /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i, // Fire Phone
835
+ ],
836
+ [
837
+ [MODEL, /(.+)/g, 'Fire Phone $1'],
838
+ [VENDOR, AMAZON],
839
+ [TYPE, MOBILE],
840
+ ],
841
+ [
842
+ // BlackBerry
843
+ /(playbook);[-\w\),; ]+(rim)/i, // BlackBerry PlayBook
844
+ ],
845
+ [MODEL, VENDOR, [TYPE, TABLET]],
846
+ [
847
+ /\b((?:bb[a-f]|st[hv])100-\d)/i,
848
+ /\(bb10; (\w+)/i, // BlackBerry 10
849
+ ],
850
+ [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]],
851
+ [
852
+ // Asus
853
+ /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i,
854
+ ],
855
+ [MODEL, [VENDOR, ASUS], [TYPE, TABLET]],
856
+ [/ (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i],
857
+ [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]],
858
+ [
859
+ // HTC
860
+ /(nexus 9)/i, // HTC Nexus 9
861
+ ],
862
+ [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]],
863
+ [
864
+ /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC
865
+ // ZTE
866
+ /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
867
+ /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i, // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
868
+ ],
869
+ [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]],
870
+ [
871
+ // TCL
872
+ /droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])\w*(\)| bui)/i,
873
+ ],
874
+ [MODEL, [VENDOR, 'TCL'], [TYPE, TABLET]],
875
+ [
876
+ // itel
877
+ /(itel) ((\w+))/i,
878
+ ],
879
+ [[VENDOR, lowerize], MODEL, [TYPE, strMapper, { tablet: ['p10001l', 'w7001'], '*': 'mobile' }]],
880
+ [
881
+ // Acer
882
+ /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i,
883
+ ],
884
+ [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]],
885
+ [
886
+ // Meizu
887
+ /droid.+; (m[1-5] note) bui/i,
888
+ /\bmz-([-\w]{2,})/i,
889
+ ],
890
+ [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]],
891
+ [
892
+ // Ulefone
893
+ /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i,
894
+ ],
895
+ [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]],
896
+ [
897
+ // Energizer
898
+ /; (energy ?\w+)(?: bui|\))/i,
899
+ /; energizer ([\w ]+)(?: bui|\))/i,
900
+ ],
901
+ [MODEL, [VENDOR, 'Energizer'], [TYPE, MOBILE]],
902
+ [
903
+ // Cat
904
+ /; cat (b35);/i,
905
+ /; (b15q?|s22 flip|s48c|s62 pro)(?: bui|\))/i,
906
+ ],
907
+ [MODEL, [VENDOR, 'Cat'], [TYPE, MOBILE]],
908
+ [
909
+ // Smartfren
910
+ /((?:new )?andromax[\w- ]+)(?: bui|\))/i,
911
+ ],
912
+ [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]],
913
+ [
914
+ // Nothing
915
+ /droid.+; (a(?:015|06[35]|142p?))/i,
916
+ ],
917
+ [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]],
918
+ [
919
+ // Archos
920
+ /; (x67 5g|tikeasy \w+|ac[1789]\d\w+)( b|\))/i,
921
+ /archos ?(5|gamepad2?|([\w ]*[t1789]|hello) ?\d+[\w ]*)( b|\))/i,
922
+ ],
923
+ [MODEL, [VENDOR, 'Archos'], [TYPE, TABLET]],
924
+ [/archos ([\w ]+)( b|\))/i, /; (ac[3-6]\d\w{2,8})( b|\))/i],
925
+ [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]],
926
+ [
927
+ // MIXED
928
+ /(imo) (tab \w+)/i, // IMO
929
+ /(infinix) (x1101b?)/i, // Infinix XPad
930
+ ],
931
+ [VENDOR, MODEL, [TYPE, TABLET]],
932
+ [
933
+ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
934
+ // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan
935
+ /; (hmd|imo) ([\w ]+?)(?: bui|\))/i, // HMD/IMO
936
+ /(hp) ([\w ]+\w)/i, // HP iPAQ
937
+ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
938
+ /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo
939
+ /(oppo) ?([\w ]+) bui/i, // OPPO
940
+ ],
941
+ [VENDOR, MODEL, [TYPE, MOBILE]],
942
+ [
943
+ /(kobo)\s(ereader|touch)/i, // Kobo
944
+ /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
945
+ /(kindle)\/([\w\.]+)/i, // Kindle
946
+ /(nook)[\w ]+build\/(\w+)/i, // Nook
947
+ /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
948
+ /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
949
+ /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
950
+ /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
951
+ /(vodafone) ([\w ]+)(?:\)| bui)/i, // Vodafone
952
+ ],
953
+ [VENDOR, MODEL, [TYPE, TABLET]],
954
+ [
955
+ /(surface duo)/i, // Surface Duo
956
+ ],
957
+ [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]],
958
+ [
959
+ /droid [\d\.]+; (fp\du?)(?: b|\))/i, // Fairphone
960
+ ],
961
+ [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]],
962
+ [
963
+ /(u304aa)/i, // AT&T
964
+ ],
965
+ [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]],
966
+ [
967
+ /\bsie-(\w*)/i, // Siemens
968
+ ],
969
+ [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]],
970
+ [
971
+ /\b(rct\w+) b/i, // RCA Tablets
972
+ ],
973
+ [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]],
974
+ [
975
+ /\b(venue[\d ]{2,7}) b/i, // Dell Venue Tablets
976
+ ],
977
+ [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]],
978
+ [
979
+ /\b(q(?:mv|ta)\w+) b/i, // Verizon Tablet
980
+ ],
981
+ [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]],
982
+ [
983
+ /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i, // Barnes & Noble Tablet
984
+ ],
985
+ [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]],
986
+ [/\b(tm\d{3}\w+) b/i],
987
+ [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]],
988
+ [
989
+ /\b(k88) b/i, // ZTE K Series Tablet
990
+ ],
991
+ [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]],
992
+ [
993
+ /\b(nx\d{3}j) b/i, // ZTE Nubia
994
+ ],
995
+ [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]],
996
+ [
997
+ /\b(gen\d{3}) b.+49h/i, // Swiss GEN Mobile
998
+ ],
999
+ [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]],
1000
+ [
1001
+ /\b(zur\d{3}) b/i, // Swiss ZUR Tablet
1002
+ ],
1003
+ [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]],
1004
+ [
1005
+ /\b((zeki)?tb.*\b) b/i, // Zeki Tablets
1006
+ ],
1007
+ [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]],
1008
+ [
1009
+ /\b([yr]\d{2}) b/i,
1010
+ /\b(dragon[- ]+touch |dt)(\w{5}) b/i, // Dragon Touch Tablet
1011
+ ],
1012
+ [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]],
1013
+ [
1014
+ /\b(ns-?\w{0,9}) b/i, // Insignia Tablets
1015
+ ],
1016
+ [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]],
1017
+ [
1018
+ /\b((nxa|next)-?\w{0,9}) b/i, // NextBook Tablets
1019
+ ],
1020
+ [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]],
1021
+ [
1022
+ /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i, // Voice Xtreme Phones
1023
+ ],
1024
+ [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]],
1025
+ [
1026
+ /\b(lvtel\-)?(v1[12]) b/i, // LvTel Phones
1027
+ ],
1028
+ [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]],
1029
+ [
1030
+ /\b(ph-1) /i, // Essential PH-1
1031
+ ],
1032
+ [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]],
1033
+ [
1034
+ /\b(v(100md|700na|7011|917g).*\b) b/i, // Envizen Tablets
1035
+ ],
1036
+ [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]],
1037
+ [
1038
+ /\b(trio[-\w\. ]+) b/i, // MachSpeed Tablets
1039
+ ],
1040
+ [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]],
1041
+ [
1042
+ /\btu_(1491) b/i, // Rotor Tablets
1043
+ ],
1044
+ [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]],
1045
+ [
1046
+ /((?:tegranote|shield t(?!.+d tv))[\w- ]*?)(?: b|\))/i, // Nvidia Tablets
1047
+ ],
1048
+ [MODEL, [VENDOR, NVIDIA], [TYPE, TABLET]],
1049
+ [
1050
+ /(sprint) (\w+)/i, // Sprint Phones
1051
+ ],
1052
+ [VENDOR, MODEL, [TYPE, MOBILE]],
1053
+ [
1054
+ /(kin\.[onetw]{3})/i, // Microsoft Kin
1055
+ ],
1056
+ [
1057
+ [MODEL, /\./g, ' '],
1058
+ [VENDOR, MICROSOFT],
1059
+ [TYPE, MOBILE],
1060
+ ],
1061
+ [
1062
+ /droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i, // Zebra
1063
+ ],
1064
+ [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]],
1065
+ [/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i],
1066
+ [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]],
1067
+ [
1068
+ ///////////////////
1069
+ // SMARTTVS
1070
+ ///////////////////
1071
+ /smart-tv.+(samsung)/i, // Samsung
1072
+ ],
1073
+ [VENDOR, [TYPE, SMARTTV]],
1074
+ [/hbbtv.+maple;(\d+)/i],
1075
+ [
1076
+ [MODEL, /^/, 'SmartTV'],
1077
+ [VENDOR, SAMSUNG],
1078
+ [TYPE, SMARTTV],
1079
+ ],
1080
+ [
1081
+ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i, // LG SmartTV
1082
+ ],
1083
+ [
1084
+ [VENDOR, LG],
1085
+ [TYPE, SMARTTV],
1086
+ ],
1087
+ [
1088
+ /(apple) ?tv/i, // Apple TV
1089
+ ],
1090
+ [VENDOR, [MODEL, APPLE + ' TV'], [TYPE, SMARTTV]],
1091
+ [
1092
+ /crkey/i, // Google Chromecast
1093
+ ],
1094
+ [
1095
+ [MODEL, CHROME + 'cast'],
1096
+ [VENDOR, GOOGLE],
1097
+ [TYPE, SMARTTV],
1098
+ ],
1099
+ [
1100
+ /droid.+aft(\w+)( bui|\))/i, // Fire TV
1101
+ ],
1102
+ [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]],
1103
+ [
1104
+ /(shield \w+ tv)/i, // Nvidia Shield TV
1105
+ ],
1106
+ [MODEL, [VENDOR, NVIDIA], [TYPE, SMARTTV]],
1107
+ [
1108
+ /\(dtv[\);].+(aquos)/i,
1109
+ /(aquos-tv[\w ]+)\)/i, // Sharp
1110
+ ],
1111
+ [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],
1112
+ [
1113
+ /(bravia[\w ]+)( bui|\))/i, // Sony
1114
+ ],
1115
+ [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]],
1116
+ [
1117
+ /(mi(tv|box)-?\w+) bui/i, // Xiaomi
1118
+ ],
1119
+ [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]],
1120
+ [
1121
+ /Hbbtv.*(technisat) (.*);/i, // TechniSAT
1122
+ ],
1123
+ [VENDOR, MODEL, [TYPE, SMARTTV]],
1124
+ [
1125
+ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
1126
+ /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i, // HbbTV devices
1127
+ ],
1128
+ [
1129
+ [VENDOR, trimStr],
1130
+ [MODEL, trimStr],
1131
+ [TYPE, SMARTTV],
1132
+ ],
1133
+ [
1134
+ // SmartTV from Unidentified Vendors
1135
+ /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i,
1136
+ ],
1137
+ [MODEL, [TYPE, SMARTTV]],
1138
+ [/\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i],
1139
+ [[TYPE, SMARTTV]],
1140
+ [
1141
+ ///////////////////
1142
+ // CONSOLES
1143
+ ///////////////////
1144
+ /(ouya)/i, // Ouya
1145
+ /(nintendo) ([wids3utch]+)/i, // Nintendo
1146
+ ],
1147
+ [VENDOR, MODEL, [TYPE, CONSOLE]],
1148
+ [
1149
+ /droid.+; (shield)( bui|\))/i, // Nvidia Portable
1150
+ ],
1151
+ [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]],
1152
+ [
1153
+ /(playstation \w+)/i, // Playstation
1154
+ ],
1155
+ [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]],
1156
+ [
1157
+ /\b(xbox(?: one)?(?!; xbox))[\); ]/i, // Microsoft Xbox
1158
+ ],
1159
+ [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]],
1160
+ [
1161
+ ///////////////////
1162
+ // WEARABLES
1163
+ ///////////////////
1164
+ /\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i, // Samsung Galaxy Watch
1165
+ ],
1166
+ [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]],
1167
+ [
1168
+ /((pebble))app/i, // Pebble
1169
+ /(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i, // Asus ZenWatch / LG Watch / Pixel Watch
1170
+ ],
1171
+ [VENDOR, MODEL, [TYPE, WEARABLE]],
1172
+ [
1173
+ /(ow(?:19|20)?we?[1-3]{1,3})/i, // Oppo Watch
1174
+ ],
1175
+ [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]],
1176
+ [
1177
+ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i, // Apple Watch
1178
+ ],
1179
+ [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]],
1180
+ [
1181
+ /(opwwe\d{3})/i, // OnePlus Watch
1182
+ ],
1183
+ [MODEL, [VENDOR, ONEPLUS], [TYPE, WEARABLE]],
1184
+ [
1185
+ /(moto 360)/i, // Motorola 360
1186
+ ],
1187
+ [MODEL, [VENDOR, MOTOROLA], [TYPE, WEARABLE]],
1188
+ [
1189
+ /(smartwatch 3)/i, // Sony SmartWatch
1190
+ ],
1191
+ [MODEL, [VENDOR, SONY], [TYPE, WEARABLE]],
1192
+ [
1193
+ /(g watch r)/i, // LG G Watch R
1194
+ ],
1195
+ [MODEL, [VENDOR, LG], [TYPE, WEARABLE]],
1196
+ [/droid.+; (wt63?0{2,3})\)/i],
1197
+ [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]],
1198
+ [
1199
+ ///////////////////
1200
+ // XR
1201
+ ///////////////////
1202
+ /droid.+; (glass) \d/i, // Google Glass
1203
+ ],
1204
+ [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]],
1205
+ [
1206
+ /(pico) (4|neo3(?: link|pro)?)/i, // Pico
1207
+ ],
1208
+ [VENDOR, MODEL, [TYPE, WEARABLE]],
1209
+ [
1210
+ /; (quest( \d| pro)?)/i, // Oculus Quest
1211
+ ],
1212
+ [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]],
1213
+ [
1214
+ ///////////////////
1215
+ // EMBEDDED
1216
+ ///////////////////
1217
+ /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i, // Tesla
1218
+ ],
1219
+ [VENDOR, [TYPE, EMBEDDED]],
1220
+ [
1221
+ /(aeobc)\b/i, // Echo Dot
1222
+ ],
1223
+ [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]],
1224
+ [
1225
+ /(homepod).+mac os/i, // Apple HomePod
1226
+ ],
1227
+ [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]],
1228
+ [/windows iot/i],
1229
+ [[TYPE, EMBEDDED]],
1230
+ [
1231
+ ////////////////////
1232
+ // MIXED (GENERIC)
1233
+ ///////////////////
1234
+ /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i, // Android Phones from Unidentified Vendors
1235
+ ],
1236
+ [MODEL, [TYPE, MOBILE]],
1237
+ [
1238
+ /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i, // Android Tablets from Unidentified Vendors
1239
+ ],
1240
+ [MODEL, [TYPE, TABLET]],
1241
+ [
1242
+ /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i, // Unidentifiable Tablet
1243
+ ],
1244
+ [[TYPE, TABLET]],
1245
+ [
1246
+ /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i, // Unidentifiable Mobile
1247
+ ],
1248
+ [[TYPE, MOBILE]],
1249
+ [
1250
+ /droid .+?; ([\w\. -]+)( bui|\))/i, // Generic Android Device
1251
+ ],
1252
+ [MODEL, [VENDOR, 'Generic']],
1253
+ ],
1254
+ engine: [
1255
+ [
1256
+ /windows.+ edge\/([\w\.]+)/i, // EdgeHTML
1257
+ ],
1258
+ [VERSION, [NAME, EDGE + 'HTML']],
1259
+ [
1260
+ /(arkweb)\/([\w\.]+)/i, // ArkWeb
1261
+ ],
1262
+ [NAME, VERSION],
1263
+ [
1264
+ /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i, // Blink
1265
+ ],
1266
+ [VERSION, [NAME, 'Blink']],
1267
+ [
1268
+ /(presto)\/([\w\.]+)/i, // Presto
1269
+ /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo
1270
+ /ekioh(flow)\/([\w\.]+)/i, // Flow
1271
+ /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
1272
+ /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab
1273
+ /\b(libweb)/i, // LibWeb
1274
+ ],
1275
+ [NAME, VERSION],
1276
+ [/ladybird\//i],
1277
+ [[NAME, 'LibWeb']],
1278
+ [
1279
+ /rv\:([\w\.]{1,9})\b.+(gecko)/i, // Gecko
1280
+ ],
1281
+ [VERSION, NAME],
1282
+ ],
1283
+ os: [
1284
+ [
1285
+ // Windows
1286
+ /microsoft (windows) (vista|xp)/i, // Windows (iTunes)
1287
+ ],
1288
+ [NAME, VERSION],
1289
+ [
1290
+ /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i, // Windows Phone
1291
+ ],
1292
+ [NAME, [VERSION, strMapper, windowsVersionMap]],
1293
+ [
1294
+ /windows nt 6\.2; (arm)/i, // Windows RT
1295
+ /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i,
1296
+ /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i,
1297
+ ],
1298
+ [
1299
+ [VERSION, strMapper, windowsVersionMap],
1300
+ [NAME, UAOSName.Windows],
1301
+ ],
1302
+ [
1303
+ // iOS/macOS
1304
+ /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
1305
+ /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
1306
+ /cfnetwork\/.+darwin/i,
1307
+ ],
1308
+ [
1309
+ [VERSION, /_/g, '.'],
1310
+ [NAME, UAOSName.iOS],
1311
+ ],
1312
+ [
1313
+ /(mac os x) ?([\w\. ]*)/i,
1314
+ /(macintosh|mac_powerpc\b)(?!.+haiku)/i, // Mac OS
1315
+ ],
1316
+ [
1317
+ [NAME, MAC_OS],
1318
+ [VERSION, /_/g, '.'],
1319
+ ],
1320
+ [
1321
+ // Mobile OSes
1322
+ /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i, // Android-x86/HarmonyOS
1323
+ ],
1324
+ [VERSION, NAME],
1325
+ [
1326
+ /(ubuntu) ([\w\.]+) like android/i, // Ubuntu Touch
1327
+ ],
1328
+ [[NAME, /(.+)/, '$1 Touch'], VERSION],
1329
+ [
1330
+ // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen
1331
+ /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/; ]?([\d\.]*)/i,
1332
+ ],
1333
+ [NAME, VERSION],
1334
+ [
1335
+ /\(bb(10);/i, // BlackBerry 10
1336
+ ],
1337
+ [VERSION, [NAME, BLACKBERRY]],
1338
+ [
1339
+ /(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i, // Symbian
1340
+ ],
1341
+ [VERSION, [NAME, 'Symbian']],
1342
+ [
1343
+ /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i, // Firefox OS
1344
+ ],
1345
+ [VERSION, [NAME, FIREFOX + ' OS']],
1346
+ [
1347
+ /web0s;.+rt(tv)/i,
1348
+ /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS
1349
+ ],
1350
+ [VERSION, [NAME, 'webOS']],
1351
+ [
1352
+ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i, // watchOS
1353
+ ],
1354
+ [VERSION, [NAME, UAOSName.WatchOS]],
1355
+ [
1356
+ // Google Chromecast
1357
+ /crkey\/([\d\.]+)/i, // Google Chromecast
1358
+ ],
1359
+ [VERSION, [NAME, CHROME + 'cast']],
1360
+ [
1361
+ /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i, // Chromium OS
1362
+ ],
1363
+ [[NAME, CHROMIUM_OS], VERSION],
1364
+ [
1365
+ // Smart TVs
1366
+ /panasonic;(viera)/i, // Panasonic Viera
1367
+ /(netrange)mmh/i, // Netrange
1368
+ /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
1369
+ // Console
1370
+ /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation
1371
+ /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
1372
+ // Other
1373
+ /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm
1374
+ /(mint)[\/\(\) ]?(\w*)/i, // Mint
1375
+ /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux
1376
+ /([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,
1377
+ // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire
1378
+ /(hurd|linux)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux
1379
+ /(gnu) ?([\w\.]*)/i, // GNU
1380
+ /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly
1381
+ /(haiku) (\w+)/i, // Haiku
1382
+ ],
1383
+ [NAME, VERSION],
1384
+ [
1385
+ /(sunos) ?([\w\.\d]*)/i, // Solaris
1386
+ ],
1387
+ [[NAME, 'Solaris'], VERSION],
1388
+ [
1389
+ /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
1390
+ /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX
1391
+ /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS
1392
+ /(unix) ?([\w\.]*)/i, // UNIX
1393
+ ],
1394
+ [NAME, VERSION],
1395
+ ],
1396
+ };