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