1+ import { UUID } from "@codewithkyle/uuid" ;
2+
3+ export type NetworkType = "4g" | "3g" | "2g" | "slow-2g" ;
4+
5+ export type DOMState = "loading" | "idling" | "booting" ;
6+
7+ export type Browser = "chrome" | "safari" | "edge" | "chromium-edge" | "ie" | "firefox" | "unknown" | "opera" ;
8+
19class Environment {
10+ public connection : NetworkType ;
11+ public cpu : number ;
12+ public memory : number | null ;
13+ public domState : DOMState ;
14+ public dataSaver : boolean ;
15+ public browser : Browser ;
16+ private tickets : Array < string > ;
17+
18+ constructor ( ) {
19+ this . memory = 4 ;
20+ this . cpu = window . navigator ?. hardwareConcurrency || 2 ;
21+ this . connection = "4g" ;
22+ this . domState = "booting" ;
23+ this . dataSaver = false ;
24+ this . browser = "unknown" ;
25+ this . tickets = [ ] ;
26+ }
27+
28+ public boot ( ) {
29+ this . setBrowser ( ) ;
30+
31+ if ( "connection" in navigator ) {
32+ // @ts -ignore
33+ this . connection = window . navigator . connection . effectiveType ;
34+ // @ts -ignore
35+ this . dataSaver = window . navigator . connection . saveData ;
36+ // @ts -ignore
37+ navigator . connection . onchange = this . handleNetworkChange . bind ( this ) ;
38+ }
39+
40+ if ( "deviceMemory" in navigator ) {
41+ // @ts -ignore
42+ this . memory = window . navigator . deviceMemory ;
43+ }
44+
45+ if ( this . tickets . length ) {
46+ this . setDOMState ( "loading" ) ;
47+ } else {
48+ this . setDOMState ( "idling" ) ;
49+ }
50+ }
51+
52+ private handleNetworkChange : EventListener = ( ) => {
53+ // @ts -ignore
54+ this . connection = window . navigator . connection . effectiveType ;
55+ sessionStorage . removeItem ( "connection-choice" ) ;
56+ } ;
57+
58+ /**
59+ * Attempts to set the DOM to the `idling` state. The DOM will only idle when all `startLoading()` methods have been resolved.
60+ * @param ticket - the `string` the was provided by the `startLoading()` method.
61+ */
62+ public stopLoading ( ticket : string ) : void {
63+ if ( ! ticket || typeof ticket !== "string" ) {
64+ console . error ( `A ticket with the typeof 'string' is required to end the loading state.` ) ;
65+ return ;
66+ }
67+
68+ const index = this . tickets . indexOf ( ticket ) ;
69+ if ( index !== - 1 ) {
70+ this . tickets . splice ( index ) ;
71+ }
72+
73+ if ( this . tickets . length === 0 && this . domState === "loading" ) {
74+ this . setDOMState ( "idling" ) ;
75+ }
76+ }
77+
78+ /**
79+ * Sets the DOM to the `soft-loading` state.
80+ * @returns a ticket `string` that is required to stop the loading state.
81+ */
82+ public startLoading ( ) : string {
83+ if ( this . domState === "idling" ) {
84+ this . setDOMState ( "loading" ) ;
85+ }
86+ const ticket = UUID ( ) ;
87+ this . tickets . push ( ticket ) ;
88+ return ticket ;
89+ }
90+
91+ /**
92+ * Sets the DOMs state attribute.
93+ * DO NOT USE THIS METHOD. DO NOT MANUALLY SET THE DOMs STATE.
94+ * @param newState - the new state of the document element
95+ */
96+ private setDOMState ( newState : DOMState ) : void {
97+ this . domState = newState ;
98+ if ( this . domState !== "loading" ) {
99+ this . tickets = [ ] ;
100+ }
101+ document . documentElement . setAttribute ( "state" , this . domState ) ;
102+ }
103+
104+ /**
105+ * Checks if the provided connection is greater than or equal to the current conneciton.
106+ * @param requiredConnection - network connection string
107+ */
108+ public checkConnection ( requiredConnection ) : boolean {
109+ let passed = false ;
110+ switch ( requiredConnection ) {
111+ case "4g" :
112+ if ( this . connection !== "2g" && this . connection !== "slow-2g" && this . connection !== "3g" ) {
113+ passed = true ;
114+ }
115+ break ;
116+ case "3g" :
117+ if ( this . connection !== "2g" && this . connection !== "slow-2g" ) {
118+ passed = true ;
119+ }
120+ break ;
121+ case "2g" :
122+ if ( this . connection !== "slow-2g" ) {
123+ passed = true ;
124+ }
125+ break ;
126+ case "slow-2g" :
127+ passed = true ;
128+ break ;
129+ default :
130+ passed = true ;
131+ break ;
132+ }
133+ return passed ;
134+ }
135+
136+ private setBrowser ( ) {
137+ // @ts -ignore
138+ const isOpera = ( ! ! window . opr && ! ! opr . addons ) || ! ! window . opera || navigator . userAgent . indexOf ( " OPR/" ) >= 0 ;
139+
140+ // @ts -ignore
141+ const isFirefox = typeof InstallTrigger !== "undefined" ;
142+
143+ const isSafari =
144+ // @ts -ignore
145+ / c o n s t r u c t o r / i. test ( window . HTMLElement ) ||
146+ ( function ( p ) {
147+ return p . toString ( ) === "[object SafariRemoteNotification]" ;
148+ // @ts -ignore
149+ } ) ( ! window [ "safari" ] || ( typeof safari !== "undefined" && safari . pushNotification ) ) ;
150+
151+ // @ts -ignore
152+ const isIE = /*@cc_on !@*/ false || ! ! document . documentMode ;
153+
154+ // @ts -ignore
155+ const isEdge = ! isIE && ! ! window . StyleMedia ;
156+
157+ // @ts -ignore
158+ const isChrome = ! ! window . chrome ;
159+
160+ const isEdgeChromium = isChrome && navigator . userAgent . indexOf ( "Edg" ) != - 1 ;
161+
162+ if ( isOpera ) {
163+ this . browser = "opera" ;
164+ } else if ( isFirefox ) {
165+ this . browser = "firefox" ;
166+ } else if ( isSafari ) {
167+ this . browser = "safari" ;
168+ } else if ( isIE ) {
169+ this . browser = "ie" ;
170+ } else if ( isEdge ) {
171+ this . browser = "edge" ;
172+ } else if ( isChrome ) {
173+ this . browser = "chrome" ;
174+ } else if ( isEdgeChromium ) {
175+ this . browser = "chromium-edge" ;
176+ } else {
177+ this . browser = "unknown" ;
178+ }
179+ document . documentElement . setAttribute ( "browser" , this . browser ) ;
180+ }
181+
182+ /**
183+ * Binds the custom element to the class.
184+ * @deprecated use `bind()` instead.
185+ */
2186 public mount ( tagName : string , constructor : CustomElementConstructor ) {
187+ this . bind ( tagName , constructor ) ;
188+ }
189+ /**
190+ * Registers a Web Component by binding the Custom Element's tag name to the provided class.
191+ */
192+ public bind ( tagName : string , constructor : CustomElementConstructor ) {
3193 if ( ! customElements . get ( tagName ) ) {
4194 customElements . define ( tagName , constructor ) ;
5195 }
@@ -53,4 +243,4 @@ class Environment {
53243 }
54244}
55245const env = new Environment ( ) ;
56- export { env as default } ;
246+ export { env as default } ;
0 commit comments