UNPKG

5.39 kBPlain TextView Raw
1const appleIphone = /iPhone/i;
2const appleIpod = /iPod/i;
3const appleTablet = /iPad/i;
4const appleUniversal = /\biOS-universal(?:.+)Mac\b/i;
5const androidPhone = /\bAndroid(?:.+)Mobile\b/i; // Match 'Android' AND 'Mobile'
6const androidTablet = /Android/i;
7const amazonPhone = /(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i; // Match 'Silk' AND 'Mobile'
8const amazonTablet = /Silk/i;
9const windowsPhone = /Windows Phone/i;
10const windowsTablet = /\bWindows(?:.+)ARM\b/i; // Match 'Windows' AND 'ARM'
11const otherBlackBerry = /BlackBerry/i;
12const otherBlackBerry10 = /BB10/i;
13const otherOpera = /Opera Mini/i;
14const otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i;
15const otherFirefox = /Mobile(?:.+)Firefox\b/i; // Match 'Mobile' AND 'Firefox'
16
17export type UserAgent = string;
18export type Navigator = {
19 userAgent: string;
20 platform: string;
21 maxTouchPoints?: number;
22};
23
24const isAppleTabletOnIos13 = (navigator?: Navigator): boolean => {
25 return (
26 typeof navigator !== 'undefined' &&
27 navigator.platform === 'MacIntel' &&
28 typeof navigator.maxTouchPoints === 'number' &&
29 navigator.maxTouchPoints > 1 &&
30 typeof MSStream === 'undefined'
31 );
32};
33
34function createMatch(userAgent: UserAgent): (regex: RegExp) => boolean {
35 return (regex: RegExp): boolean => regex.test(userAgent);
36}
37
38export type isMobileResult = {
39 apple: {
40 phone: boolean;
41 ipod: boolean;
42 tablet: boolean;
43 universal: boolean;
44 device: boolean;
45 };
46 amazon: {
47 phone: boolean;
48 tablet: boolean;
49 device: boolean;
50 };
51 android: {
52 phone: boolean;
53 tablet: boolean;
54 device: boolean;
55 };
56 windows: {
57 phone: boolean;
58 tablet: boolean;
59 device: boolean;
60 };
61 other: {
62 blackberry: boolean;
63 blackberry10: boolean;
64 opera: boolean;
65 firefox: boolean;
66 chrome: boolean;
67 device: boolean;
68 };
69 phone: boolean;
70 tablet: boolean;
71 any: boolean;
72};
73
74export type IsMobileParameter = UserAgent | Navigator;
75
76export default function isMobile(param?: IsMobileParameter): isMobileResult {
77 let nav: Navigator = {
78 userAgent: '',
79 platform: '',
80 maxTouchPoints: 0,
81 };
82
83 if (!param && typeof navigator !== 'undefined') {
84 nav = {
85 userAgent: navigator.userAgent,
86 platform: navigator.platform,
87 maxTouchPoints: navigator.maxTouchPoints || 0,
88 };
89 } else if (typeof param === 'string') {
90 nav.userAgent = param;
91 } else if (param && param.userAgent) {
92 nav = {
93 userAgent: param.userAgent,
94 platform: param.platform,
95 maxTouchPoints: param.maxTouchPoints || 0,
96 };
97 }
98
99 let userAgent = nav.userAgent;
100
101 // Facebook mobile app's integrated browser adds a bunch of strings that
102 // match everything. Strip it out if it exists.
103 let tmp = userAgent.split('[FBAN');
104 if (typeof tmp[1] !== 'undefined') {
105 userAgent = tmp[0];
106 }
107
108 // Twitter mobile app's integrated browser on iPad adds a "Twitter for
109 // iPhone" string. Same probably happens on other tablet platforms.
110 // This will confuse detection so strip it out if it exists.
111 tmp = userAgent.split('Twitter');
112 if (typeof tmp[1] !== 'undefined') {
113 userAgent = tmp[0];
114 }
115
116 const match = createMatch(userAgent);
117
118 const result: isMobileResult = {
119 apple: {
120 phone: match(appleIphone) && !match(windowsPhone),
121 ipod: match(appleIpod),
122 tablet:
123 !match(appleIphone) &&
124 (match(appleTablet) || isAppleTabletOnIos13(nav)) &&
125 !match(windowsPhone),
126 universal: match(appleUniversal),
127 device:
128 (match(appleIphone) ||
129 match(appleIpod) ||
130 match(appleTablet) ||
131 match(appleUniversal) ||
132 isAppleTabletOnIos13(nav)) &&
133 !match(windowsPhone),
134 },
135 amazon: {
136 phone: match(amazonPhone),
137 tablet: !match(amazonPhone) && match(amazonTablet),
138 device: match(amazonPhone) || match(amazonTablet),
139 },
140 android: {
141 phone:
142 (!match(windowsPhone) && match(amazonPhone)) ||
143 (!match(windowsPhone) && match(androidPhone)),
144 tablet:
145 !match(windowsPhone) &&
146 !match(amazonPhone) &&
147 !match(androidPhone) &&
148 (match(amazonTablet) || match(androidTablet)),
149 device:
150 (!match(windowsPhone) &&
151 (match(amazonPhone) ||
152 match(amazonTablet) ||
153 match(androidPhone) ||
154 match(androidTablet))) ||
155 match(/\bokhttp\b/i),
156 },
157 windows: {
158 phone: match(windowsPhone),
159 tablet: match(windowsTablet),
160 device: match(windowsPhone) || match(windowsTablet),
161 },
162 other: {
163 blackberry: match(otherBlackBerry),
164 blackberry10: match(otherBlackBerry10),
165 opera: match(otherOpera),
166 firefox: match(otherFirefox),
167 chrome: match(otherChrome),
168 device:
169 match(otherBlackBerry) ||
170 match(otherBlackBerry10) ||
171 match(otherOpera) ||
172 match(otherFirefox) ||
173 match(otherChrome),
174 },
175 any: false,
176 phone: false,
177 tablet: false,
178 };
179
180 result.any =
181 result.apple.device ||
182 result.android.device ||
183 result.windows.device ||
184 result.other.device;
185 // excludes 'other' devices and ipods, targeting touchscreen phones
186 result.phone =
187 result.apple.phone || result.android.phone || result.windows.phone;
188 result.tablet =
189 result.apple.tablet || result.android.tablet || result.windows.tablet;
190
191 return result;
192}