1 | /*
|
2 | Copyright 2013-2015 ASIAL CORPORATION
|
3 |
|
4 | Licensed under the Apache License, Version 2.0 (the "License");
|
5 | you may not use this file except in compliance with the License.
|
6 | You may obtain a copy of the License at
|
7 |
|
8 | http://www.apache.org/licenses/LICENSE-2.0
|
9 |
|
10 | Unless required by applicable law or agreed to in writing, software
|
11 | distributed under the License is distributed on an "AS IS" BASIS,
|
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 | See the License for the specific language governing permissions and
|
14 | limitations under the License.
|
15 |
|
16 | */
|
17 |
|
18 | // Save HTMLElement object before Custom Elements polyfill patch global HTMLElement.
|
19 | const NativeHTMLElement = window.HTMLElement;
|
20 |
|
21 | /**
|
22 | * @object ons.platform
|
23 | * @category util
|
24 | * @description
|
25 | * [en]Utility methods to detect current platform.[/en]
|
26 | * [ja]現在実行されているプラットフォームを検知するためのユーティリティメソッドを収めたオブジェクトです。[/ja]
|
27 | */
|
28 | class Platform {
|
29 |
|
30 | /**
|
31 | * All elements will be rendered as if the app was running on this platform.
|
32 | * @type {String}
|
33 | */
|
34 | constructor() {
|
35 | this._selectedPlatform = null;
|
36 | this._ignorePlatformSelect = false;
|
37 | }
|
38 |
|
39 | /**
|
40 | * @method select
|
41 | * @signature select(platform)
|
42 | * @param {string} platform Name of the platform.
|
43 | * [en]Possible values are: "opera", "firefox", "safari", "chrome", "ie", "android", "blackberry", "ios" or "wp".[/en]
|
44 | * [ja]"opera", "firefox", "safari", "chrome", "ie", "android", "blackberry", "ios", "wp"のいずれかを指定します。[/ja]
|
45 | * @description
|
46 | * [en]Sets the platform used to render the elements. Useful for testing.[/en]
|
47 | * [ja]要素を描画するために利用するプラットフォーム名を設定します。テストに便利です。[/ja]
|
48 | */
|
49 | select(platform) {
|
50 | if (typeof platform === 'string') {
|
51 | this._selectedPlatform = platform.trim().toLowerCase();
|
52 | }
|
53 | }
|
54 |
|
55 | _getSelectedPlatform() {
|
56 | return this._ignorePlatformSelect ? null : this._selectedPlatform;
|
57 | }
|
58 |
|
59 | _runOnActualPlatform(fn) {
|
60 | this._ignorePlatformSelect = true;
|
61 | const result = fn();
|
62 | this._ignorePlatformSelect = false;
|
63 |
|
64 | return result;
|
65 | }
|
66 |
|
67 | //----------------
|
68 | // General
|
69 | //----------------
|
70 | /**
|
71 | * @method isWebView
|
72 | * @signature isWebView()
|
73 | * @description
|
74 | * [en]Returns whether app is running in Cordova.[/en]
|
75 | * [ja]Cordova内で実行されているかどうかを返します。[/ja]
|
76 | * @return {Boolean}
|
77 | */
|
78 | isWebView() {
|
79 | if (document.readyState === 'loading' || document.readyState == 'uninitialized') {
|
80 | throw new Error('isWebView() method is available after dom contents loaded.');
|
81 | }
|
82 |
|
83 | return !!(window.cordova || window.phonegap || window.PhoneGap);
|
84 | }
|
85 |
|
86 | //----------------
|
87 | // iOS devices
|
88 | //----------------
|
89 | /**
|
90 | * @method isIPhone
|
91 | * @signature isIPhone()
|
92 | * @description
|
93 | * [en]Returns whether the device is iPhone.[/en]
|
94 | * [ja]iPhone上で実行されているかどうかを返します。[/ja]
|
95 | * @return {Boolean}
|
96 | */
|
97 | isIPhone() {
|
98 | return /iPhone/i.test(navigator.userAgent);
|
99 | }
|
100 |
|
101 | /**
|
102 | * @method isIPhoneX
|
103 | * @signature isIPhoneX()
|
104 | * @description
|
105 | * [en]Returns whether the device is iPhone X, XS, XS Max, XR, 11, 11 Pro, 11 Pro Max, 12 Mini, 12, 12 Pro or 12 Pro Max.[/en]
|
106 | * [ja]iPhone X や XS、XS Max、XR、11、11 Pro、11 Pro Max、12 Mini、12、12 Pro、または12 Pro Max上で実行されているかどうかを返します。[/ja]
|
107 | * @return {Boolean}
|
108 | */
|
109 | isIPhoneX() {
|
110 | // iOS WebViews on the same iOS version have the same user agent.
|
111 | // We cannot avoid using window.screen.
|
112 | // We also cannot use cordova-plugin-device since its behavior is different between simulators and real devices.
|
113 | // This works well both in iOS Safari and (UI|WK)WebView of iPhone X.
|
114 | return this.isIPhone() &&
|
115 | (
|
116 | // X, XS, 11 Pro, 12 Mini
|
117 | window.screen.width === 375 && window.screen.height === 812 || // portrait
|
118 | window.screen.width === 812 && window.screen.height === 375 || // landscape
|
119 |
|
120 | // XS Max, XR, 11, 11 Pro Max
|
121 | window.screen.width === 414 && window.screen.height === 896 || // portrait
|
122 | window.screen.width === 896 && window.screen.height === 414 || // landscape
|
123 |
|
124 | // 12, 12 Pro
|
125 | window.screen.width === 390 && window.screen.height === 844 || // portrait
|
126 | window.screen.width === 844 && window.screen.height === 390 || // landscape
|
127 |
|
128 | // 12 Pro Max
|
129 | window.screen.width === 428 && window.screen.height === 926 || // portrait
|
130 | window.screen.width === 926 && window.screen.height === 428 // landscape
|
131 | );
|
132 | }
|
133 |
|
134 | /**
|
135 | * @method isIPad
|
136 | * @signature isIPad()
|
137 | * @description
|
138 | * [en]Returns whether the device is iPad.[/en]
|
139 | * [ja]iPad上で実行されているかどうかを返します。[/ja]
|
140 | * @return {Boolean}
|
141 | */
|
142 | isIPad() {
|
143 | return /iPad/i.test(navigator.userAgent) || this.isIPadOS();
|
144 | }
|
145 |
|
146 | /**
|
147 | * @return {Boolean}
|
148 | */
|
149 | isIPod() {
|
150 | return /iPod/i.test(navigator.userAgent);
|
151 | }
|
152 |
|
153 | //----------------
|
154 | // iOS versions
|
155 | //----------------
|
156 | /**
|
157 | * @method isIOS
|
158 | * @signature isIOS([forceActualPlatform])
|
159 | * @param {Boolean} forceActualPlatform
|
160 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
161 | * [ja][/ja]
|
162 | * @description
|
163 | * [en]Returns whether the OS is iOS. By default will return manually selected platform if it is set.[/en]
|
164 | * [ja]iOS上で実行されているかどうかを返します。[/ja]
|
165 | * @return {Boolean}
|
166 | */
|
167 | isIOS(forceActualPlatform) {
|
168 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
169 | return this._getSelectedPlatform() === 'ios';
|
170 | }
|
171 |
|
172 | if (typeof device === 'object' && !/browser/i.test(device.platform)) {
|
173 | return /iOS/i.test(device.platform);
|
174 | } else {
|
175 | return /iPhone|iPad|iPod/i.test(navigator.userAgent) || this.isIPadOS();
|
176 | }
|
177 | }
|
178 |
|
179 | /**
|
180 | * @method isIOS7above
|
181 | * @signature isIOS7above()
|
182 | * @description
|
183 | * [en]Returns whether the iOS version is 7 or above.[/en]
|
184 | * [ja]iOS7以上で実行されているかどうかを返します。[/ja]
|
185 | * @return {Boolean}
|
186 | */
|
187 | isIOS7above() {
|
188 | if (typeof device === 'object' && !/browser/i.test(device.platform)) {
|
189 | return (/iOS/i.test(device.platform) && (parseInt(device.version.split('.')[0]) >= 7));
|
190 | } else if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) {
|
191 | const ver = (navigator.userAgent.match(/\b[0-9]+_[0-9]+(?:_[0-9]+)?\b/) || [''])[0].replace(/_/g, '.');
|
192 | return (parseInt(ver.split('.')[0]) >= 7);
|
193 | }
|
194 | return false;
|
195 | }
|
196 |
|
197 | /**
|
198 | * @method isIPadOS
|
199 | * @signature isIPadOS()
|
200 | * @description
|
201 | * [en]Returns whether the OS is iPadOS.[/en]
|
202 | * [ja][/ja]
|
203 | * @return {Boolean}
|
204 | */
|
205 | isIPadOS() {
|
206 | // The iPadOS User Agent string is the same as MacOS so as a
|
207 | // workaround we test the max touch points, which is 5 for
|
208 | // iPads and 0 for desktop browsers.
|
209 | return !!(/Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints && navigator.maxTouchPoints === 5);
|
210 | }
|
211 |
|
212 | //----------------
|
213 | // iOS browsers
|
214 | //----------------
|
215 | /**
|
216 | * @method isIOSSafari
|
217 | * @signature isIOSSafari()
|
218 | * @description
|
219 | * [en]Returns whether app is running in iOS Safari.[/en]
|
220 | * [ja]iOS Safariで実行されているかどうかを返します。[/ja]
|
221 | * @return {Boolean}
|
222 | */
|
223 | isIOSSafari() {
|
224 | const navigator = window.navigator;
|
225 | const ua = navigator.userAgent;
|
226 |
|
227 | return !!(this.isIOS() && ua.indexOf('Safari') !== -1 && ua.indexOf('Version') !== -1 && !navigator.standalone);
|
228 | }
|
229 |
|
230 | /**
|
231 | * @method isWKWebView
|
232 | * @signature isWKWebView()
|
233 | * @description
|
234 | * [en]Returns whether app is running in WKWebView.[/en]
|
235 | * [ja]WKWebViewで実行されているかどうかを返します。[/ja]
|
236 | * @return {Boolean}
|
237 | */
|
238 | isWKWebView() {
|
239 | const lte9 = /constructor/i.test(NativeHTMLElement);
|
240 | return !!(this.isIOS() && window.webkit && window.webkit.messageHandlers && window.indexedDB && !lte9);
|
241 | }
|
242 |
|
243 | //----------------
|
244 | // Android devices
|
245 | //----------------
|
246 | /**
|
247 | * @method isAndroidPhone
|
248 | * @signature isAndroidPhone()
|
249 | * @description
|
250 | * [en]Returns whether the device is Android phone.[/en]
|
251 | * [ja]Android携帯上で実行されているかどうかを返します。[/ja]
|
252 | * @return {Boolean}
|
253 | */
|
254 | isAndroidPhone() {
|
255 | return /Android/i.test(navigator.userAgent) && /Mobile/i.test(navigator.userAgent);
|
256 | }
|
257 |
|
258 | /**
|
259 | * @method isAndroidTablet
|
260 | * @signature isAndroidTablet()
|
261 | * @description
|
262 | * [en]Returns whether the device is Android tablet.[/en]
|
263 | * [ja]Androidタブレット上で実行されているかどうかを返します。[/ja]
|
264 | * @return {Boolean}
|
265 | */
|
266 | isAndroidTablet() {
|
267 | return /Android/i.test(navigator.userAgent) && !/Mobile/i.test(navigator.userAgent);
|
268 | }
|
269 |
|
270 | //----------------
|
271 | // Android versions
|
272 | //----------------
|
273 | /**
|
274 | * @method isAndroid
|
275 | * @signature isAndroid([forceActualPlatform])
|
276 | * @param {Boolean} forceActualPlatform
|
277 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
278 | * [ja][/ja]
|
279 | * @description
|
280 | * [en]Returns whether the OS is Android. By default will return manually selected platform if it is set.[/en]
|
281 | * [ja]Android上で実行されているかどうかを返します。[/ja]
|
282 | * @return {Boolean}
|
283 | */
|
284 | isAndroid(forceActualPlatform) {
|
285 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
286 | return this._getSelectedPlatform() === 'android';
|
287 | }
|
288 |
|
289 | if (typeof device === 'object' && !/browser/i.test(device.platform)) {
|
290 | return /Android/i.test(device.platform);
|
291 | } else {
|
292 | return /Android/i.test(navigator.userAgent);
|
293 | }
|
294 | }
|
295 |
|
296 | //----------------
|
297 | // Other devices
|
298 | //----------------
|
299 | /**
|
300 | * @method isWP
|
301 | * @signature isWP([forceActualPlatform])
|
302 | * @param {Boolean} forceActualPlatform
|
303 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
304 | * [ja][/ja]
|
305 | * @description
|
306 | * [en]Returns whether the OS is Windows phone. By default will return manually selected platform if it is set.[/en]
|
307 | * [ja][/ja]
|
308 | * @return {Boolean}
|
309 | */
|
310 | isWP(forceActualPlatform) {
|
311 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
312 | return this._getSelectedPlatform() === 'wp';
|
313 | }
|
314 |
|
315 | if (typeof device === 'object' && !/browser/i.test(device.platform)) {
|
316 | return /Win32NT|WinCE/i.test(device.platform);
|
317 | } else {
|
318 | return /Windows Phone|IEMobile|WPDesktop/i.test(navigator.userAgent);
|
319 | }
|
320 | }
|
321 |
|
322 | /**
|
323 | * @method isBlackBerry
|
324 | * @signature isBlackBerry([forceActualPlatform])
|
325 | * @param {Boolean} forceActualPlatform
|
326 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
327 | * [ja][/ja]
|
328 | * @description
|
329 | * [en]Returns whether the device is BlackBerry. By default will return manually selected platform if it is set.[/en]
|
330 | * [ja]BlackBerry上で実行されているかどうかを返します。[/ja]
|
331 | * @return {Boolean}
|
332 | */
|
333 | isBlackBerry(forceActualPlatform) {
|
334 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
335 | return this._getSelectedPlatform() === 'blackberry';
|
336 | }
|
337 |
|
338 | if (typeof device === 'object' && !/browser/i.test(device.platform)) {
|
339 | return /BlackBerry/i.test(device.platform);
|
340 | } else {
|
341 | return /BlackBerry|RIM Tablet OS|BB10/i.test(navigator.userAgent);
|
342 | }
|
343 | }
|
344 |
|
345 | //----------------
|
346 | // Other browsers
|
347 | //----------------
|
348 | /**
|
349 | * @method isOpera
|
350 | * @signature isOpera([forceActualPlatform])
|
351 | * @param {Boolean} forceActualPlatform
|
352 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
353 | * [ja][/ja]
|
354 | * @description
|
355 | * [en]Returns whether the browser is Opera. By default will return manually selected platform if it is set.[/en]
|
356 | * [ja]Opera上で実行されているかどうかを返します。[/ja]
|
357 | * @return {Boolean}
|
358 | */
|
359 | isOpera(forceActualPlatform) {
|
360 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
361 | return this._getSelectedPlatform() === 'opera';
|
362 | }
|
363 |
|
364 | return (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0);
|
365 | }
|
366 |
|
367 | /**
|
368 | * @method isFirefox
|
369 | * @signature isFirefox([forceActualPlatform])
|
370 | * @param {Boolean} forceActualPlatform
|
371 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
372 | * [ja][/ja]
|
373 | * @description
|
374 | * [en]Returns whether the browser is Firefox. By default will return manually selected platform if it is set.[/en]
|
375 | * [ja]Firefox上で実行されているかどうかを返します。[/ja]
|
376 | * @return {Boolean}
|
377 | */
|
378 | isFirefox(forceActualPlatform) {
|
379 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
380 | return this._getSelectedPlatform() === 'firefox';
|
381 | }
|
382 |
|
383 | return (typeof InstallTrigger !== 'undefined');
|
384 | }
|
385 |
|
386 | /**
|
387 | * @method isSafari
|
388 | * @signature isSafari([forceActualPlatform])
|
389 | * @param {Boolean} forceActualPlatform
|
390 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
391 | * [ja][/ja]
|
392 | * @description
|
393 | * [en]Returns whether the browser is Safari. By default will return manually selected platform if it is set.[/en]
|
394 | * [ja]Safari上で実行されているかどうかを返します。[/ja]
|
395 | * @return {Boolean}
|
396 | */
|
397 | isSafari(forceActualPlatform) {
|
398 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
399 | return this._getSelectedPlatform() === 'safari';
|
400 | }
|
401 |
|
402 | return (Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) || (function (p) { return p.toString() === '[object SafariRemoteNotification]'; })(!window['safari'] || safari.pushNotification);
|
403 | }
|
404 |
|
405 | /**
|
406 | * @method isChrome
|
407 | * @signature isChrome([forceActualPlatform])
|
408 | * @param {Boolean} forceActualPlatform
|
409 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
410 | * [ja][/ja]
|
411 | * @description
|
412 | * [en]Returns whether the browser is Chrome. By default will return manually selected platform if it is set.[/en]
|
413 | * [ja]Chrome上で実行されているかどうかを返します。[/ja]
|
414 | * @return {Boolean}
|
415 | */
|
416 | isChrome(forceActualPlatform) {
|
417 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
418 | return this._getSelectedPlatform() === 'chrome';
|
419 | }
|
420 |
|
421 | return (!!window.chrome && !(!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) && !(navigator.userAgent.indexOf(' Edge/') >= 0));
|
422 | }
|
423 |
|
424 | /**
|
425 | * @method isIE
|
426 | * @signature isIE([forceActualPlatform])
|
427 | * @param {Boolean} forceActualPlatform
|
428 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
429 | * [ja][/ja]
|
430 | * @description
|
431 | * [en]Returns whether the browser is Internet Explorer. By default will return manually selected platform if it is set.[/en]
|
432 | * [ja]Internet Explorer上で実行されているかどうかを返します。[/ja]
|
433 | * @return {Boolean}
|
434 | */
|
435 | isIE(forceActualPlatform) {
|
436 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
437 | return this._getSelectedPlatform() === 'ie';
|
438 | }
|
439 |
|
440 | return false || !!document.documentMode;
|
441 | }
|
442 |
|
443 | /**
|
444 | * @method isEdge
|
445 | * @signature isEdge([forceActualPlatform])
|
446 | * @param {Boolean} forceActualPlatform
|
447 | * [en]If true, selected platform is ignored and the actual platform is returned.[/en]
|
448 | * [ja][/ja]
|
449 | * @description
|
450 | * [en]Returns whether the browser is Edge. By default will return manually selected platform if it is set.[/en]
|
451 | * [ja]Edge上で実行されているかどうかを返します。[/ja]
|
452 | * @return {Boolean}
|
453 | */
|
454 | isEdge(forceActualPlatform) {
|
455 | if (!forceActualPlatform && this._getSelectedPlatform()) {
|
456 | return this._getSelectedPlatform() === 'edge';
|
457 | }
|
458 |
|
459 | return navigator.userAgent.indexOf(' Edge/') >= 0;
|
460 | }
|
461 |
|
462 | //----------------
|
463 | // Utility functions
|
464 | //----------------
|
465 | /**
|
466 | * @return {String}
|
467 | */
|
468 | getMobileOS() {
|
469 | if (this.isAndroid()) {
|
470 | return 'android';
|
471 | }
|
472 | else if (this.isIOS()) {
|
473 | return 'ios';
|
474 | }
|
475 | else if (this.isWP()) {
|
476 | return 'wp';
|
477 | }
|
478 | else {
|
479 | return 'other';
|
480 | }
|
481 | }
|
482 |
|
483 | /**
|
484 | * @return {String}
|
485 | */
|
486 | getIOSDevice() {
|
487 | if (this.isIPhone()) {
|
488 | return 'iphone';
|
489 | }
|
490 | else if (this.isIPad()) {
|
491 | return 'ipad';
|
492 | }
|
493 | else if (this.isIPod()) {
|
494 | return 'ipod';
|
495 | }
|
496 | else {
|
497 | return 'na';
|
498 | }
|
499 | }
|
500 | }
|
501 |
|
502 |
|
503 | export default new Platform();
|