FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
ionic/src/platform/platform.ts at master · javascript2016/ionic · GitHub
javascript2016
ionic
Repository navigation
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
ionic
/
src
/
platform
/
platform.ts
Copy path
More file actions
More file actions
Latest commit
History
History
History
1207 lines (1038 loc) · 33.4 KB
Breadcrumbs
ionic
/
src
/
platform
/
platform.ts
Copy path
File metadata and controls
1207 lines (1038 loc) · 33.4 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import
{
EventEmitter
,
NgZone
}
from
'@angular/core'
;
import
{
getCss
,
isTextInput
}
from
'../util/dom'
;
import
{
QueryParams
}
from
'./query-params'
;
import
{
removeArrayItem
}
from
'../util/util'
;
export
type
DocumentDirection
=
'ltr'
|
'rtl'
;
/**
*
@name
Platform
*
@description
* The Platform service can be used to get information about your current device.
* You can get all of the platforms associated with the device using the [platforms](#platforms)
* method, including whether the app is being viewed from a tablet, if it's
* on a mobile device or browser, and the exact platform (iOS, Android, etc).
* You can also get the orientation of the device, if it uses right-to-left
* language direction, and much much more. With this information you can completely
* customize your app to fit any device.
*
*
@usage
* ```ts
* import { Platform } from 'ionic-angular';
*
*
@Component
({...})
* export MyPage {
* constructor(public platform: Platform) {
*
* }
* }
* ```
*
@demo
/docs/demos/src/platform/
*/
export
class
Platform
{
private
_win
:
Window
;
private
_doc
:
HTMLDocument
;
private
_versions
:
{
[
name
:
string
]
:
PlatformVersion
}
=
{
}
;
private
_dir
:
DocumentDirection
;
private
_lang
:
string
;
private
_ua
:
string
;
private
_qp
=
new
QueryParams
(
)
;
private
_nPlt
:
string
;
private
_readyPromise
:
Promise
<
any
>
;
private
_readyResolve
:
any
;
private
_bbActions
:
BackButtonAction
[
]
=
[
]
;
private
_registry
:
{
[
name
:
string
]
:
PlatformConfig
}
;
private
_default
:
string
;
private
_pW
=
0
;
private
_pH
=
0
;
private
_lW
=
0
;
private
_lH
=
0
;
private
_isPortrait
:
boolean
=
null
;
private
_uiEvtOpts
=
false
;
/**
@hidden
*/
zone
:
NgZone
;
/**
@internal
*/
Css
:
{
transform
?:
string
;
transition
?:
string
;
transitionDuration
?:
string
;
transitionDelay
?:
string
;
transitionTimingFn
?:
string
;
transitionStart
?:
string
;
transitionEnd
?:
string
;
transformOrigin
?:
string
;
animationDelay
?:
string
;
}
;
/**
@internal
*/
_platforms
:
string
[
]
=
[
]
;
constructor
(
)
{
this
.
_readyPromise
=
new
Promise
(
res
=>
{
this
.
_readyResolve
=
res
;
}
)
;
this
.
backButton
.
subscribe
(
(
)
=>
{
// the hardware back button event has been fired
console
.
debug
(
'hardware back button'
)
;
// decide which backbutton action should run
this
.
runBackButtonAction
(
)
;
}
)
;
}
/**
*
@hidden
*/
setWindow
(
win
:
Window
)
{
this
.
_win
=
win
;
}
/**
*
@hidden
*/
win
(
)
{
return
this
.
_win
;
}
/**
*
@hidden
*/
setDocument
(
doc
:
HTMLDocument
)
{
this
.
_doc
=
doc
;
}
/**
*
@hidden
*/
doc
(
)
{
return
this
.
_doc
;
}
/**
*
@hidden
*/
setZone
(
zone
:
NgZone
)
{
this
.
zone
=
zone
;
}
/**
*
@hidden
*/
setCssProps
(
docElement
:
HTMLElement
)
{
this
.
Css
=
getCss
(
docElement
)
;
}
// Methods
// **********************************************
/**
*
@returns
{
boolean
} returns true/false based on platform.
*
@description
* Depending on the platform the user is on, `is(platformName)` will
* return `true` or `false`. Note that the same app can return `true`
* for more than one platform name. For example, an app running from
* an iPad would return `true` for the platform names: `mobile`,
* `ios`, `ipad`, and `tablet`. Additionally, if the app was running
* from Cordova then `cordova` would be true, and if it was running
* from a web browser on the iPad then `mobileweb` would be `true`.
*
* ```
* import { Platform } from 'ionic-angular';
*
*
@Component
({...})
* export MyPage {
* constructor(public platform: Platform) {
* if (this.platform.is('ios')) {
* // This will only print when on iOS
* console.log('I am an iOS device!');
* }
* }
* }
* ```
*
* | Platform Name | Description |
* |-----------------|------------------------------------|
* | android | on a device running Android. |
* | cordova | on a device running Cordova. |
* | core | on a desktop device. |
* | ios | on a device running iOS. |
* | ipad | on an iPad device. |
* | iphone | on an iPhone device. |
* | mobile | on a mobile device. |
* | mobileweb | in a browser on a mobile device. |
* | phablet | on a phablet device. |
* | tablet | on a tablet device. |
* | windows | on a device running Windows. |
*
*
@param
{
string
} platformName
*/
is
(
platformName
:
string
)
:
boolean
{
return
(
this
.
_platforms
.
indexOf
(
platformName
)
>
-
1
)
;
}
/**
*
@returns
{
array
} the array of platforms
*
@description
* Depending on what device you are on, `platforms` can return multiple values.
* Each possible value is a hierarchy of platforms. For example, on an iPhone,
* it would return `mobile`, `ios`, and `iphone`.
*
* ```
* import { Platform } from 'ionic-angular';
*
*
@Component
({...})
* export MyPage {
* constructor(public platform: Platform) {
* // This will print an array of the current platforms
* console.log(this.platform.platforms());
* }
* }
* ```
*/
platforms
(
)
:
Array
<
string
>
{
// get the array of active platforms, which also knows the hierarchy,
// with the last one the most important
return
this
.
_platforms
;
}
/**
* Returns an object containing version information about all of the platforms.
*
* ```
* import { Platform } from 'ionic-angular';
*
*
@Component
({...})
* export MyPage {
* constructor(public platform: Platform) {
* // This will print an object containing
* // all of the platforms and their versions
* console.log(platform.versions());
* }
* }
* ```
*
*
@returns
{
object
} An object containing all of the platforms and their versions.
*/
versions
(
)
:
{
[
name
:
string
]
:
PlatformVersion
}
{
// get all the platforms that have a valid parsed version
return
this
.
_versions
;
}
/**
*
@hidden
*/
version
(
)
:
PlatformVersion
{
for
(
var
platformName
in
this
.
_versions
)
{
if
(
this
.
_versions
[
platformName
]
)
{
return
this
.
_versions
[
platformName
]
;
}
}
return
{
}
;
}
/**
* Returns a promise when the platform is ready and native functionality
* can be called. If the app is running from within a web browser, then
* the promise will resolve when the DOM is ready. When the app is running
* from an application engine such as Cordova, then the promise will
* resolve when Cordova triggers the `deviceready` event.
*
* The resolved value is the `readySource`, which states which platform
* ready was used. For example, when Cordova is ready, the resolved ready
* source is `cordova`. The default ready source value will be `dom`. The
* `readySource` is useful if different logic should run depending on the
* platform the app is running from. For example, only Cordova can execute
* the status bar plugin, so the web should not run status bar plugin logic.
*
* ```
* import { Component } from '@angular/core';
* import { Platform } from 'ionic-angular';
*
*
@Component
({...})
* export MyApp {
* constructor(public platform: Platform) {
* this.platform.ready().then((readySource) => {
* console.log('Platform ready from', readySource);
* // Platform now ready, execute any required native code
* });
* }
* }
* ```
*
@returns
{
promise
}
*/
ready
(
)
:
Promise
<
string
>
{
return
this
.
_readyPromise
;
}
/**
*
@hidden
* This should be triggered by the engine when the platform is
* ready. If there was no custom prepareReady method from the engine,
* such as Cordova or Electron, then it uses the default DOM ready.
*/
triggerReady
(
readySource
:
string
)
{
this
.
zone
.
run
(
(
)
=>
{
this
.
_readyResolve
(
readySource
)
;
}
)
;
}
/**
*
@hidden
* This is the default prepareReady if it's not replaced by an engine,
* such as Cordova or Electron. If there was no custom prepareReady
* method from an engine then it uses the method below, which triggers
* the platform ready on the DOM ready event, and the default resolved
* value is `dom`.
*/
prepareReady
(
)
{
const
self
=
this
;
if
(
self
.
_doc
.
readyState
===
'complete'
||
self
.
_doc
.
readyState
===
'interactive'
)
{
self
.
triggerReady
(
'dom'
)
;
}
else
{
self
.
_doc
.
addEventListener
(
'DOMContentLoaded'
,
completed
,
false
)
;
self
.
_win
.
addEventListener
(
'load'
,
completed
,
false
)
;
}
function
completed
(
)
{
self
.
_doc
.
removeEventListener
(
'DOMContentLoaded'
,
completed
,
false
)
;
self
.
_win
.
removeEventListener
(
'load'
,
completed
,
false
)
;
self
.
triggerReady
(
'dom'
)
;
}
}
/**
* Set the app's language direction, which will update the `dir` attribute
* on the app's root `<html>` element. We recommend the app's `index.html`
* file already has the correct `dir` attribute value set, such as
* `<html dir="ltr">` or `<html dir="rtl">`. This method is useful if the
* direction needs to be dynamically changed per user/session.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
*
@param
{
DocumentDirection
} dir Examples: `rtl`, `ltr`
*
@param
{
boolean
} updateDocument
*/
setDir
(
dir
:
DocumentDirection
,
updateDocument
:
boolean
)
{
this
.
_dir
=
dir
;
this
.
isRTL
=
(
dir
===
'rtl'
)
;
if
(
updateDocument
!==
false
)
{
this
.
_doc
[
'documentElement'
]
.
setAttribute
(
'dir'
,
dir
)
;
}
}
/**
* Returns app's language direction.
* We recommend the app's `index.html` file already has the correct `dir`
* attribute value set, such as `<html dir="ltr">` or `<html dir="rtl">`.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
*
@returns
{
DocumentDirection
}
*/
dir
(
)
:
DocumentDirection
{
return
this
.
_dir
;
}
/**
* Returns if this app is using right-to-left language direction or not.
* We recommend the app's `index.html` file already has the correct `dir`
* attribute value set, such as `<html dir="ltr">` or `<html dir="rtl">`.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
*
@returns
{
boolean
}
*/
isRTL
:
boolean
;
/**
* Set the app's language and optionally the country code, which will update
* the `lang` attribute on the app's root `<html>` element.
* We recommend the app's `index.html` file already has the correct `lang`
* attribute value set, such as `<html lang="en">`. This method is useful if
* the language needs to be dynamically changed per user/session.
* [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations)
*
@param
{
string
} language Examples: `en-US`, `en-GB`, `ar`, `de`, `zh`, `es-MX`
*
@param
{
boolean
} updateDocument Specifies whether the `lang` attribute of `<html>` should be updated
*/
setLang
(
language
:
string
,
updateDocument
:
boolean
)
{
this
.
_lang
=
language
;
if
(
updateDocument
!==
false
)
{
this
.
_doc
[
'documentElement'
]
.
setAttribute
(
'lang'
,
language
)
;
}
}
/**
* Returns app's language and optional country code.
* We recommend the app's `index.html` file already has the correct `lang`
* attribute value set, such as `<html lang="en">`.
* [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations)
*
@returns
{
string
}
*/
lang
(
)
:
string
{
return
this
.
_lang
;
}
// Methods meant to be overridden by the engine
// **********************************************
// Provided NOOP methods so they do not error when
// called by engines (the browser)that do not provide them
/**
*
@hidden
*/
exitApp
(
)
{
}
// Events meant to be triggered by the engine
// **********************************************
/**
*
@hidden
*/
backButton
:
EventEmitter
<
Event
>
=
new
EventEmitter
<
Event
>
(
)
;
/**
* The pause event emits when the native platform puts the application
* into the background, typically when the user switches to a different
* application. This event would emit when a Cordova app is put into
* the background, however, it would not fire on a standard web browser.
*/
pause
:
EventEmitter
<
Event
>
=
new
EventEmitter
<
Event
>
(
)
;
/**
* The resume event emits when the native platform pulls the application
* out from the background. This event would emit when a Cordova app comes
* out from the background, however, it would not fire on a standard web browser.
*/
resume
:
EventEmitter
<
Event
>
=
new
EventEmitter
<
Event
>
(
)
;
/**
* The resize event emits when the browser window has changed dimensions. This
* could be from a browser window being physically resized, or from a device
* changing orientation.
*/
resize
:
EventEmitter
<
Event
>
=
new
EventEmitter
<
Event
>
(
)
;
/**
* The back button event is triggered when the user presses the native
* platform's back button, also referred to as the "hardware" back button.
* This event is only used within Cordova apps running on Android and
* Windows platforms. This event is not fired on iOS since iOS doesn't come
* with a hardware back button in the same sense an Android or Windows device
* does.
*
* Registering a hardware back button action and setting a priority allows
* apps to control which action should be called when the hardware back
* button is pressed. This method decides which of the registered back button
* actions has the highest priority and should be called.
*
*
@param
{
Function
} fn Called when the back button is pressed,
* if this registered action has the highest priority.
*
@param
{
number
} priority Set the priority for this action. Only the highest priority will execute. Defaults to `0`.
*
@returns
{
Function
} A function that, when called, will unregister
* the back button action.
*/
registerBackButtonAction
(
fn
:
Function
,
priority
:
number
=
0
)
:
Function
{
const
action
:
BackButtonAction
=
{
fn
,
priority
}
;
this
.
_bbActions
.
push
(
action
)
;
// return a function to unregister this back button action
return
(
)
=>
{
removeArrayItem
(
this
.
_bbActions
,
action
)
;
}
;
}
/**
*
@hidden
*/
runBackButtonAction
(
)
{
// decide which one back button action should run
let
winner
:
BackButtonAction
=
null
;
this
.
_bbActions
.
forEach
(
(
action
:
BackButtonAction
)
=>
{
if
(
!
winner
||
action
.
priority
>=
winner
.
priority
)
{
winner
=
action
;
}
}
)
;
// run the winning action if there is one
winner
&&
winner
.
fn
&&
winner
.
fn
(
)
;
}
// Getter/Setter Methods
// **********************************************
/**
*
@hidden
*/
setUserAgent
(
userAgent
:
string
)
{
this
.
_ua
=
userAgent
;
}
/**
*
@hidden
*/
setQueryParams
(
url
:
string
)
{
this
.
_qp
.
parseUrl
(
url
)
;
}
/**
* Get the query string parameter
*/
getQueryParam
(
key
:
string
)
{
return
this
.
_qp
.
get
(
key
)
;
}
/**
* Get the current url.
*/
url
(
)
{
return
this
.
_win
[
'location'
]
[
'href'
]
;
}
/**
*
@hidden
*/
userAgent
(
)
:
string
{
return
this
.
_ua
||
''
;
}
/**
*
@hidden
*/
setNavigatorPlatform
(
navigatorPlt
:
string
)
{
this
.
_nPlt
=
navigatorPlt
;
}
/**
*
@hidden
*/
navigatorPlatform
(
)
:
string
{
return
this
.
_nPlt
||
''
;
}
/**
* Gets the width of the platform's viewport using `window.innerWidth`.
* Using this method is preferred since the dimension is a cached value,
* which reduces the chance of multiple and expensive DOM reads.
*/
width
(
)
:
number
{
this
.
_calcDim
(
)
;
return
this
.
_isPortrait
?
this
.
_pW
:
this
.
_lW
;
}
/**
* Gets the height of the platform's viewport using `window.innerHeight`.
* Using this method is preferred since the dimension is a cached value,
* which reduces the chance of multiple and expensive DOM reads.
*/
height
(
)
:
number
{
this
.
_calcDim
(
)
;
return
this
.
_isPortrait
?
this
.
_pH
:
this
.
_lH
;
}
/**
*
@hidden
*/
getElementComputedStyle
(
ele
:
HTMLElement
,
pseudoEle
?:
string
)
{
return
this
.
_win
[
'getComputedStyle'
]
(
ele
,
pseudoEle
)
;
}
/**
*
@hidden
*/
getElementFromPoint
(
x
:
number
,
y
:
number
)
{
return
<
HTMLElement
>
this
.
_doc
[
'elementFromPoint'
]
(
x
,
y
)
;
}
/**
*
@hidden
*/
getElementBoundingClientRect
(
ele
:
HTMLElement
)
{
return
ele
[
'getBoundingClientRect'
]
(
)
;
}
/**
* Returns `true` if the app is in portait mode.
*/
isPortrait
(
)
:
boolean
{
this
.
_calcDim
(
)
;
return
this
.
_isPortrait
;
}
/**
* Returns `true` if the app is in landscape mode.
*/
isLandscape
(
)
:
boolean
{
return
!
this
.
isPortrait
(
)
;
}
private
_calcDim
(
)
{
// we're caching window dimensions so that
// we're not forcing many layouts
// if _isPortrait is null then that means
// the dimensions needs to be looked up again
// this also has to cover an edge case that only
// happens on iOS 10 (not other versions of iOS)
// where window.innerWidth is always bigger than
// window.innerHeight when it is first measured,
// even when the device is in portrait but
// the second time it is measured it is correct.
// Hopefully this check will not be needed in the future
if
(
this
.
_isPortrait
===
null
||
this
.
_isPortrait
===
false
&&
this
.
_win
[
'innerWidth'
]
<
this
.
_win
[
'innerHeight'
]
)
{
var
win
=
this
.
_win
;
var
innerWidth
=
win
[
'innerWidth'
]
;
var
innerHeight
=
win
[
'innerHeight'
]
;
// we're keeping track of portrait and landscape dimensions
// separately because the virtual keyboard can really mess
// up accurate values when the keyboard is up
if
(
win
.
screen
.
width
>
0
&&
win
.
screen
.
height
>
0
)
{
if
(
innerWidth
<
innerHeight
)
{
// the device is in portrait
// we have to do fancier checking here
// because of the virtual keyboard resizing
// the window
if
(
this
.
_pW
<=
innerWidth
)
{
console
.
debug
(
'setting _isPortrait to true'
)
;
this
.
_isPortrait
=
true
;
this
.
_pW
=
innerWidth
;
}
if
(
this
.
_pH
<=
innerHeight
)
{
console
.
debug
(
'setting _isPortrait to true'
)
;
this
.
_isPortrait
=
true
;
this
.
_pH
=
innerHeight
;
}
}
else
{
// the device is in landscape
if
(
this
.
_lW
!==
innerWidth
)
{
console
.
debug
(
'setting _isPortrait to false'
)
;
this
.
_isPortrait
=
false
;
this
.
_lW
=
innerWidth
;
}
if
(
this
.
_lH
!==
innerHeight
)
{
console
.
debug
(
'setting _isPortrait to false'
)
;
this
.
_isPortrait
=
false
;
this
.
_lH
=
innerHeight
;
}
}
}
}
}
/**
*
@hidden
* This requestAnimationFrame will NOT be wrapped by zone.
*/
raf
(
callback
:
{
(
timeStamp
?:
number
)
:
void
}
|
Function
)
:
number
{
const
win
:
any
=
this
.
_win
;
return
win
[
'__zone_symbol__requestAnimationFrame'
]
(
callback
)
;
}
/**
*
@hidden
*/
cancelRaf
(
rafId
:
number
)
{
const
win
:
any
=
this
.
_win
;
return
win
[
'__zone_symbol__cancelAnimationFrame'
]
(
rafId
)
;
}
/**
*
@hidden
* This setTimeout will NOT be wrapped by zone.
*/
timeout
(
callback
:
Function
,
timeout
?:
number
)
:
number
{
const
win
:
any
=
this
.
_win
;
return
win
[
'__zone_symbol__setTimeout'
]
(
callback
,
timeout
)
;
}
/**
*
@hidden
* This setTimeout will NOT be wrapped by zone.
*/
cancelTimeout
(
timeoutId
:
number
)
{
const
win
:
any
=
this
.
_win
;
win
[
'__zone_symbol__clearTimeout'
]
(
timeoutId
)
;
}
/**
*
@hidden
* Built to use modern event listener options, like "passive".
* If options are not supported, then just return a boolean which
* represents "capture". Returns a method to remove the listener.
*/
registerListener
(
ele
:
any
,
eventName
:
string
,
callback
:
{
(
ev
?:
UIEvent
)
:
void
}
,
opts
:
EventListenerOptions
,
unregisterListenersCollection
?:
Function
[
]
)
:
Function
{
// use event listener options when supported
// otherwise it's just a boolean for the "capture" arg
const
listenerOpts
:
any
=
this
.
_uiEvtOpts
?
{
'capture'
:
!
!
opts
.
capture
,
'passive'
:
!
!
opts
.
passive
,
}
:
!
!
opts
.
capture
;
let
unReg
:
Function
;
if
(
!
opts
.
zone
&&
ele
[
'__zone_symbol__addEventListener'
]
)
{
// do not wrap this event in zone and we've verified we can use the raw addEventListener
ele
[
'__zone_symbol__addEventListener'
]
(
eventName
,
callback
,
listenerOpts
)
;
unReg
=
function
unregisterListener
(
)
{
ele
[
'__zone_symbol__removeEventListener'
]
(
eventName
,
callback
,
listenerOpts
)
;
}
;
}
else
{
// use the native addEventListener, which is wrapped with zone
ele
[
'addEventListener'
]
(
eventName
,
callback
,
listenerOpts
)
;
unReg
=
function
unregisterListener
(
)
{
ele
[
'removeEventListener'
]
(
eventName
,
callback
,
listenerOpts
)
;
}
;
}
if
(
unregisterListenersCollection
)
{
unregisterListenersCollection
.
push
(
unReg
)
;
}
return
unReg
;
}
/**
*
@hidden
*/
transitionEnd
(
el
:
HTMLElement
,
callback
:
{
(
ev
?:
TransitionEvent
)
:
void
}
,
zone
=
true
)
{
const
unRegs
:
Function
[
]
=
[
]
;
function
unregister
(
)
{
unRegs
.
forEach
(
unReg
=>
{
unReg
(
)
;
}
)
;
}
function
onTransitionEnd
(
ev
:
TransitionEvent
)
{
if
(
el
===
ev
.
target
)
{
unregister
(
)
;
callback
(
ev
)
;
}
}
if
(
el
)
{
this
.
registerListener
(
el
,
'webkitTransitionEnd'
,
<
any
>
onTransitionEnd
,
{
zone
:
zone
}
,
unRegs
)
;
this
.
registerListener
(
el
,
'transitionend'
,
<
any
>
onTransitionEnd
,
{
zone
:
zone
}
,
unRegs
)
;
}
return
unregister
;
}
/**
*
@hidden
*/
windowLoad
(
callback
:
Function
)
{
const
win
=
this
.
_win
;
const
doc
=
this
.
_doc
;
let
unreg
:
Function
;
if
(
doc
.
readyState
===
'complete'
)
{
callback
(
win
,
doc
)
;
}
else
{
unreg
=
this
.
registerListener
(
win
,
'load'
,
(
)
=>
{
unreg
&&
unreg
(
)
;
callback
(
win
,
doc
)
;
}
,
{
zone
:
false
}
)
;
}
}
/**
*
@hidden
*/
isActiveElement
(
ele
:
HTMLElement
)
{
return
!
!
(
ele
&&
(
this
.
getActiveElement
(
)
===
ele
)
)
;
}
/**
*
@hidden
*/
getActiveElement
(
)
{
return
this
.
_doc
[
'activeElement'
]
;
}
/**
*
@hidden
*/
hasFocus
(
ele
:
HTMLElement
)
{
return
!
!
(
(
ele
&&
(
this
.
getActiveElement
(
)
===
ele
)
)
&&
(
ele
.
parentElement
.
querySelector
(
':focus'
)
===
ele
)
)
;
}
/**
*
@hidden
*/
hasFocusedTextInput
(
)
{
const
ele
=
this
.
getActiveElement
(
)
;
if
(
isTextInput
(
ele
)
)
{
return
(
ele
.
parentElement
.
querySelector
(
':focus'
)
===
ele
)
;
}
return
false
;
}
/**
*
@hidden
*/
focusOutActiveElement
(
)
{
const
activeElement
:
any
=
this
.
getActiveElement
(
)
;
activeElement
&&
activeElement
.
blur
&&
activeElement
.
blur
(
)
;
}
private
_initEvents
(
)
{
// Test via a getter in the options object to see if the passive property is accessed
try
{
var
opts
=
Object
.
defineProperty
(
{
}
,
'passive'
,
{
get
:
(
)
=>
{
this
.
_uiEvtOpts
=
true
;
}
}
)
;
this
.
_win
.
addEventListener
(
'optsTest'
,
null
,
opts
)
;
}
catch
(
e
)
{
}
// add the window resize event listener XXms after
this
.
timeout
(
(
)
=>
{
var
timerId
:
any
;
this
.
registerListener
(
this
.
_win
,
'resize'
,
(
)
=>
{
clearTimeout
(
timerId
)
;
timerId
=
setTimeout
(
(
)
=>
{
// setting _isPortrait to null means the
// dimensions will need to be looked up again
if
(
this
.
hasFocusedTextInput
(
)
===
false
)
{
this
.
_isPortrait
=
null
;
}
this
.
zone
.
run
(
(
)
=>
this
.
resize
.
emit
(
)
)
;
}
,
200
)
;
}
,
{
passive
:
true
,
zone
:
false
}
)
;
}
,
2000
)
;
}
// Platform Registry
// **********************************************
/**
*
@hidden
*/
setPlatformConfigs
(
platformConfigs
:
{
[
key
:
string
]
:
PlatformConfig
}
)
{
this
.
_registry
=
platformConfigs
||
{
}
;
}
/**
*
@hidden
*/
getPlatformConfig
(
platformName
:
string
)
:
PlatformConfig
{
return
this
.
_registry
[
platformName
]
||
{
}
;
}
/**
*
@hidden
*/
registry
(
)
{
return
this
.
_registry
;
}
/**
*
@hidden
*/
setDefault
(
platformName
:
string
)
{
this
.
_default
=
platformName
;
}
/**
*
@hidden
*/
testQuery
(
queryValue
:
string
,
queryTestValue
:
string
)
:
boolean
{
const
valueSplit
=
queryValue
.
toLowerCase
(
)
.
split
(
';'
)
;
return
valueSplit
.
indexOf
(
queryTestValue
)
>
-
1
;
}
/**
*
@hidden
*/
testNavigatorPlatform
(
navigatorPlatformExpression
:
string
)
:
boolean
{
const
rgx
=
new
RegExp
(
navigatorPlatformExpression
,
'i'
)
;
return
rgx
.
test
(
this
.
_nPlt
)
;
}
/**
*
@hidden
*/
matchUserAgentVersion
(
userAgentExpression
:
RegExp
)
:
any
{
if
(
this
.
_ua
&&
userAgentExpression
)
{
const
val
=
this
.
_ua
.
match
(
userAgentExpression
)
;
if
(
val
)
{
return
{
major
:
val
[
1
]
,
minor
:
val
[
2
]
}
;
}
}
}
testUserAgent
(
expression
:
string
)
:
boolean
{
if
(
this
.
_ua
)
{
return
this
.
_ua
.
indexOf
(
expression
)
>=
0
;
}
return
false
;
}
/**
*
@hidden
*/
isPlatformMatch
(
queryStringName
:
string
,
userAgentAtLeastHas
?:
string
[
]
,
userAgentMustNotHave
:
string
[
]
=
[
]
)
:
boolean
{
const
queryValue
=
this
.
_qp
.
get
(
'ionicplatform'
)
;
if
(
queryValue
)
{
return
this
.
testQuery
(
queryValue
,
queryStringName
)
;
}
userAgentAtLeastHas
=
userAgentAtLeastHas
||
[
queryStringName
]
;
const
userAgent
=
this
.
_ua
.
toLowerCase
(
)
;
for
(
var
i
=
0
;
i
<
userAgentAtLeastHas
.
length
;
i
++
)
{
if
(
userAgent
.
indexOf
(
userAgentAtLeastHas
[
i
]
)
>
-
1
)
{
for
(
var
j
=
0
;
j
<
userAgentMustNotHave
.
length
;
j
++
)
{
if
(
userAgent
.
indexOf
(
userAgentMustNotHave
[
j
]
)
>
-
1
)
{
return
false
;
}
}
return
true
;
}
}
return
false
;
}
/**
@hidden
*/
init
(
)
{
this
.
_initEvents
(
)
;
let
rootPlatformNode
:
PlatformNode
;
let
enginePlatformNode
:
PlatformNode
;
// figure out the most specific platform and active engine
let
tmpPlt
:
PlatformNode
;
for
(
let
platformName
in
this
.
_registry
)
{
tmpPlt
=
this
.
matchPlatform
(
platformName
)
;
if
(
tmpPlt
)
{
// we found a platform match!
// check if its more specific than the one we already have
if
(
tmpPlt
.
isEngine
)
{
// because it matched then this should be the active engine
// you cannot have more than one active engine
enginePlatformNode
=
tmpPlt
;
}
else
if
(
!
rootPlatformNode
||
tmpPlt
.
depth
>
rootPlatformNode
.
depth
)
{
// only find the root node for platforms that are not engines
// set this node as the root since we either don't already
// have one, or this one is more specific that the current one
rootPlatformNode
=
tmpPlt
;
}
}
}
if
(
!
rootPlatformNode
)
{
rootPlatformNode
=
new
PlatformNode
(
this
.
_registry
,
this
.
_default
)
;
}
// build a Platform instance filled with the
// hierarchy of active platforms and settings
if
(
rootPlatformNode
)
{
// check if we found an engine node (cordova/node-webkit/etc)
if
(
enginePlatformNode
)
{
// add the engine to the first in the platform hierarchy
// the original rootPlatformNode now becomes a child
// of the engineNode, which is not the new root
enginePlatformNode
.
child
=
rootPlatformNode
;
rootPlatformNode
.
parent
=
enginePlatformNode
;
rootPlatformNode
=
enginePlatformNode
;
}
let
platformNode
=
rootPlatformNode
;
while
(
platformNode
)
{
insertSuperset
(
this
.
_registry
,
platformNode
)
;
platformNode
=
platformNode
.
child
;
}
// make sure the root noot is actually the root
// incase a node was inserted before the root
platformNode
=
rootPlatformNode
.
parent
;
while
(
platformNode
)
{
rootPlatformNode
=
platformNode
;
platformNode
=
platformNode
.
parent
;
}
platformNode
=
rootPlatformNode
;
while
(
platformNode
)
{
platformNode
.
initialize
(
this
)
;
// extra check for ipad pro issue
// https://forums.developer.apple.com/thread/25948
if
(
platformNode
.
name
===
'iphone'
&&
this
.
navigatorPlatform
(
)
===
'iPad'
)
{
// this is an ipad pro so push ipad and tablet to platforms
// and then return as we are done
this
.
_platforms
.
push
(
'tablet'
)
;
this
.
_platforms
.
push
(
'ipad'
)
;
return
;
}
// set the array of active platforms with
// the last one in the array the most important
this
.
_platforms
.
push
(
platformNode
.
name
)
;
// get the platforms version if a version parser was provided
this
.
_versions
[
platformNode
.
name
]
=
platformNode
.
version
(
this
)
;
// go to the next platform child
View remainder of file in raw view
Back
|
FazBrowse Home
|
New Git URL