@naturalcycles/js-lib 15.85.0 → 15.86.0

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