@hkdigital/lib-core 0.5.16 → 0.5.18

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.
Files changed (38) hide show
  1. package/dist/app/info.d.ts +26 -0
  2. package/dist/app/info.js +33 -0
  3. package/dist/app/typedef.d.ts +4 -0
  4. package/dist/app/typedef.js +8 -0
  5. package/dist/browser/info/device.d.ts +67 -0
  6. package/dist/browser/info/device.js +183 -0
  7. package/dist/browser/info/display.d.ts +73 -0
  8. package/dist/browser/info/display.js +109 -0
  9. package/dist/browser/info/engine.d.ts +39 -0
  10. package/dist/browser/info/engine.js +64 -0
  11. package/dist/browser/info/language.d.ts +18 -0
  12. package/dist/browser/info/language.js +23 -0
  13. package/dist/browser/info/system.d.ts +47 -0
  14. package/dist/browser/info/system.js +102 -0
  15. package/dist/browser/info/timezone.d.ts +20 -0
  16. package/dist/browser/info/timezone.js +25 -0
  17. package/dist/browser/info.d.ts +81 -0
  18. package/dist/browser/info.js +185 -0
  19. package/dist/browser/typedef.d.ts +31 -0
  20. package/dist/browser/typedef.js +38 -0
  21. package/dist/ui/components/game-box/GameBox.svelte +37 -90
  22. package/dist/ui/components/game-box/GameBox.svelte.d.ts +4 -70
  23. package/dist/ui/components/game-box/gamebox.util.d.ts +0 -24
  24. package/dist/ui/components/game-box/gamebox.util.js +0 -61
  25. package/dist/ui/components/game-box/typedef.d.ts +16 -0
  26. package/dist/ui/components/game-box/typedef.js +23 -0
  27. package/dist/ui/components/typedef.d.ts +1 -0
  28. package/dist/ui/components/typedef.js +1 -0
  29. package/dist/util/expect/values.d.ts +1 -1
  30. package/dist/util/expect/values.js +5 -2
  31. package/dist/util/random/bytes.js +6 -3
  32. package/dist/util/random/uuid.d.ts +22 -0
  33. package/dist/util/random/uuid.js +59 -0
  34. package/dist/util/random.d.ts +1 -0
  35. package/dist/util/random.js +2 -1
  36. package/dist/util/unique/index.d.ts +41 -6
  37. package/dist/util/unique/index.js +62 -12
  38. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Get simplified platform identifier
3
+ *
4
+ * @example
5
+ * // 'macos', 'windows', 'linux', 'ios', 'android', 'unknown'
6
+ *
7
+ * @returns {string} platform identifier
8
+ */
9
+ export function getPlatform() {
10
+ const platform = navigator.platform.toLowerCase();
11
+ const ua = navigator.userAgent.toLowerCase();
12
+
13
+ // iOS devices
14
+ if (/iphone|ipod|ipad/.test(platform)) {
15
+ return 'ios';
16
+ }
17
+
18
+ // iPadOS (reports as MacIntel but has touch)
19
+ if (platform === 'macintel' && navigator.maxTouchPoints > 2) {
20
+ return 'ios';
21
+ }
22
+
23
+ // Android
24
+ if (/android/.test(ua)) {
25
+ return 'android';
26
+ }
27
+
28
+ // macOS
29
+ if (/mac/.test(platform)) {
30
+ return 'macos';
31
+ }
32
+
33
+ // Windows
34
+ if (/win/.test(platform)) {
35
+ return 'windows';
36
+ }
37
+
38
+ // Linux
39
+ if (/linux/.test(platform)) {
40
+ return 'linux';
41
+ }
42
+
43
+ return 'unknown';
44
+ }
45
+
46
+ /**
47
+ * Get raw platform string from navigator
48
+ *
49
+ * @example
50
+ * // 'MacIntel', 'Win32', 'Linux x86_64', 'iPhone', 'iPad'
51
+ *
52
+ * @returns {string} raw platform identifier
53
+ */
54
+ export function getPlatformRaw() {
55
+ return navigator.platform;
56
+ }
57
+
58
+ /**
59
+ * Get number of CPU cores (logical processors)
60
+ *
61
+ * @example
62
+ * // Desktop: 8, 16, 24
63
+ * // Mobile: 4, 6, 8
64
+ * // Older devices: 2
65
+ *
66
+ * @returns {number} number of logical processors
67
+ */
68
+ export function getCpuCores() {
69
+ return navigator.hardwareConcurrency || 1;
70
+ }
71
+
72
+ /**
73
+ * Check if device is online
74
+ *
75
+ * @example
76
+ * // true (connected), false (offline)
77
+ *
78
+ * @returns {boolean} true if online
79
+ */
80
+ export function isOnline() {
81
+ return navigator.onLine;
82
+ }
83
+
84
+ /**
85
+ * Get connection type if available (experimental API)
86
+ *
87
+ * @example
88
+ * // '4g', '3g', 'slow-2g', 'wifi', null
89
+ *
90
+ * @returns {string|null} connection type or null
91
+ */
92
+ export function getConnectionType() {
93
+ // @ts-ignore
94
+ const connection =
95
+ navigator.connection ||
96
+ // @ts-ignore
97
+ navigator.mozConnection ||
98
+ // @ts-ignore
99
+ navigator.webkitConnection;
100
+
101
+ return connection?.effectiveType || null;
102
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Get timezone offset in minutes
3
+ *
4
+ * @example
5
+ * // UTC: 0
6
+ * // PST (UTC-8): 480
7
+ * // CET (UTC+1): -60
8
+ *
9
+ * @returns {number} timezone offset
10
+ */
11
+ export function getTimezoneOffset(): number;
12
+ /**
13
+ * Get timezone name
14
+ *
15
+ * @example
16
+ * // 'America/New_York', 'Europe/Amsterdam', 'Asia/Tokyo'
17
+ *
18
+ * @returns {string} timezone identifier
19
+ */
20
+ export function getTimezone(): string;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Get timezone offset in minutes
3
+ *
4
+ * @example
5
+ * // UTC: 0
6
+ * // PST (UTC-8): 480
7
+ * // CET (UTC+1): -60
8
+ *
9
+ * @returns {number} timezone offset
10
+ */
11
+ export function getTimezoneOffset() {
12
+ return new Date().getTimezoneOffset();
13
+ }
14
+
15
+ /**
16
+ * Get timezone name
17
+ *
18
+ * @example
19
+ * // 'America/New_York', 'Europe/Amsterdam', 'Asia/Tokyo'
20
+ *
21
+ * @returns {string} timezone identifier
22
+ */
23
+ export function getTimezone() {
24
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
25
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @typedef {{
3
+ * deviceType?: boolean,
4
+ * isMobile?: boolean,
5
+ * platform?: boolean,
6
+ * browser?: boolean,
7
+ * browserEngine?: boolean,
8
+ * screenWidth?: boolean,
9
+ * screenHeight?: boolean,
10
+ * devicePixelRatio?: boolean,
11
+ * hasTouch?: boolean,
12
+ * language?: boolean,
13
+ * timezone?: boolean,
14
+ * isPwa?: boolean,
15
+ * viewportWidth?: boolean,
16
+ * viewportHeight?: boolean,
17
+ * orientation?: boolean,
18
+ * cpuCores?: boolean,
19
+ * connectionType?: boolean
20
+ * }} BrowserInfoOptions
21
+ */
22
+ /**
23
+ * @typedef {import('./typedef.js').BrowserInfo} BrowserInfo
24
+ */
25
+ /**
26
+ * Collect browser and device information for analytics/stats
27
+ *
28
+ * @param {BrowserInfoOptions} [options] - Enable/disable individual properties
29
+ *
30
+ * @returns {BrowserInfo} collected browser information
31
+ *
32
+ * @example
33
+ * // Get all essential info (default)
34
+ * const info = getBrowserInfo();
35
+ *
36
+ * @example
37
+ * // Get only specific properties
38
+ * const info = getBrowserInfo({
39
+ * deviceType: true,
40
+ * platform: true,
41
+ * browser: true
42
+ * });
43
+ *
44
+ * @example
45
+ * // Get essential + extended properties
46
+ * const info = getBrowserInfo({
47
+ * deviceType: true,
48
+ * platform: true,
49
+ * viewportWidth: true,
50
+ * viewportHeight: true,
51
+ * orientation: true,
52
+ * cpuCores: true
53
+ * });
54
+ */
55
+ export function getBrowserInfo(options?: BrowserInfoOptions): BrowserInfo;
56
+ export * from "./info/device.js";
57
+ export * from "./info/display.js";
58
+ export * from "./info/engine.js";
59
+ export * from "./info/language.js";
60
+ export * from "./info/system.js";
61
+ export * from "./info/timezone.js";
62
+ export type BrowserInfoOptions = {
63
+ deviceType?: boolean;
64
+ isMobile?: boolean;
65
+ platform?: boolean;
66
+ browser?: boolean;
67
+ browserEngine?: boolean;
68
+ screenWidth?: boolean;
69
+ screenHeight?: boolean;
70
+ devicePixelRatio?: boolean;
71
+ hasTouch?: boolean;
72
+ language?: boolean;
73
+ timezone?: boolean;
74
+ isPwa?: boolean;
75
+ viewportWidth?: boolean;
76
+ viewportHeight?: boolean;
77
+ orientation?: boolean;
78
+ cpuCores?: boolean;
79
+ connectionType?: boolean;
80
+ };
81
+ export type BrowserInfo = import("./typedef.js").BrowserInfo;
@@ -0,0 +1,185 @@
1
+ export * from './info/device.js';
2
+ export * from './info/display.js';
3
+ export * from './info/engine.js';
4
+ export * from './info/language.js';
5
+ export * from './info/system.js';
6
+ export * from './info/timezone.js';
7
+
8
+ import { getDeviceType, getIsMobile } from './info/device.js';
9
+ import { getBrowserName, getBrowserEngine } from './info/engine.js';
10
+
11
+ import {
12
+ getScreenSize,
13
+ getViewportSize,
14
+ getDevicePixelRatio,
15
+ getOrientation,
16
+ hasTouchSupport,
17
+ getIsPwa
18
+ } from './info/display.js';
19
+
20
+ import { getLanguage } from './info/language.js';
21
+ import { getPlatform, getCpuCores, getConnectionType } from './info/system.js';
22
+ import { getTimezone } from './info/timezone.js';
23
+
24
+ /**
25
+ * @typedef {{
26
+ * deviceType?: boolean,
27
+ * isMobile?: boolean,
28
+ * platform?: boolean,
29
+ * browser?: boolean,
30
+ * browserEngine?: boolean,
31
+ * screenWidth?: boolean,
32
+ * screenHeight?: boolean,
33
+ * devicePixelRatio?: boolean,
34
+ * hasTouch?: boolean,
35
+ * language?: boolean,
36
+ * timezone?: boolean,
37
+ * isPwa?: boolean,
38
+ * viewportWidth?: boolean,
39
+ * viewportHeight?: boolean,
40
+ * orientation?: boolean,
41
+ * cpuCores?: boolean,
42
+ * connectionType?: boolean
43
+ * }} BrowserInfoOptions
44
+ */
45
+
46
+ /**
47
+ * @typedef {import('./typedef.js').BrowserInfo} BrowserInfo
48
+ */
49
+
50
+ /**
51
+ * Collect browser and device information for analytics/stats
52
+ *
53
+ * @param {BrowserInfoOptions} [options] - Enable/disable individual properties
54
+ *
55
+ * @returns {BrowserInfo} collected browser information
56
+ *
57
+ * @example
58
+ * // Get all essential info (default)
59
+ * const info = getBrowserInfo();
60
+ *
61
+ * @example
62
+ * // Get only specific properties
63
+ * const info = getBrowserInfo({
64
+ * deviceType: true,
65
+ * platform: true,
66
+ * browser: true
67
+ * });
68
+ *
69
+ * @example
70
+ * // Get essential + extended properties
71
+ * const info = getBrowserInfo({
72
+ * deviceType: true,
73
+ * platform: true,
74
+ * viewportWidth: true,
75
+ * viewportHeight: true,
76
+ * orientation: true,
77
+ * cpuCores: true
78
+ * });
79
+ */
80
+ export function getBrowserInfo(options = {}) {
81
+ // Default to BrowserInfoEssential properties
82
+ const {
83
+ deviceType = true,
84
+ isMobile = true,
85
+ platform = true,
86
+ browser = true,
87
+ browserEngine = true,
88
+ screenWidth = true,
89
+ screenHeight = true,
90
+ devicePixelRatio = true,
91
+ hasTouch = true,
92
+ language = true,
93
+ timezone = true,
94
+ isPwa = true,
95
+ // Extended properties (default false)
96
+ viewportWidth = false,
97
+ viewportHeight = false,
98
+ orientation = false,
99
+ cpuCores = false,
100
+ connectionType = false
101
+ } = options;
102
+
103
+ const info = {};
104
+
105
+ // Device type
106
+ if (deviceType) {
107
+ info.deviceType = getDeviceType();
108
+ }
109
+
110
+ // Mobile check
111
+ if (isMobile) {
112
+ info.isMobile = getIsMobile();
113
+ }
114
+
115
+ // Platform
116
+ if (platform) {
117
+ info.platform = getPlatform();
118
+ }
119
+
120
+ // Browser name
121
+ if (browser) {
122
+ info.browser = getBrowserName();
123
+ }
124
+
125
+ // Browser engine
126
+ if (browserEngine) {
127
+ info.browserEngine = getBrowserEngine();
128
+ }
129
+
130
+ // Screen dimensions
131
+ if (screenWidth || screenHeight) {
132
+ const screenSize = getScreenSize();
133
+ if (screenWidth) info.screenWidth = screenSize.width;
134
+ if (screenHeight) info.screenHeight = screenSize.height;
135
+ }
136
+
137
+ // Device pixel ratio
138
+ if (devicePixelRatio) {
139
+ info.devicePixelRatio = getDevicePixelRatio();
140
+ }
141
+
142
+ // Touch support
143
+ if (hasTouch) {
144
+ info.hasTouch = hasTouchSupport();
145
+ }
146
+
147
+ // Language
148
+ if (language) {
149
+ info.language = getLanguage();
150
+ }
151
+
152
+ // Timezone
153
+ if (timezone) {
154
+ info.timezone = getTimezone();
155
+ }
156
+
157
+ // PWA
158
+ if (isPwa) {
159
+ info.isPwa = getIsPwa();
160
+ }
161
+
162
+ // Viewport dimensions (extended)
163
+ if (viewportWidth || viewportHeight) {
164
+ const viewportSize = getViewportSize();
165
+ if (viewportWidth) info.viewportWidth = viewportSize.width;
166
+ if (viewportHeight) info.viewportHeight = viewportSize.height;
167
+ }
168
+
169
+ // Orientation (extended)
170
+ if (orientation) {
171
+ info.orientation = getOrientation();
172
+ }
173
+
174
+ // CPU cores (extended)
175
+ if (cpuCores) {
176
+ info.cpuCores = getCpuCores();
177
+ }
178
+
179
+ // Connection type (extended)
180
+ if (connectionType) {
181
+ info.connectionType = getConnectionType();
182
+ }
183
+
184
+ return info;
185
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Essential browser info
3
+ */
4
+ export type BrowserInfoEssential = {
5
+ deviceType?: "phone" | "tablet" | "tv" | "desktop";
6
+ isMobile?: boolean;
7
+ platform?: string;
8
+ browser?: string;
9
+ browserEngine?: "blink" | "gecko" | "webkit" | "unknown";
10
+ screenWidth?: number;
11
+ screenHeight?: number;
12
+ devicePixelRatio?: number;
13
+ hasTouch?: boolean;
14
+ language?: string;
15
+ timezone?: string;
16
+ isPwa?: boolean;
17
+ };
18
+ /**
19
+ * Extended browser info (opt-in)
20
+ */
21
+ export type BrowserInfoExtended = {
22
+ viewportWidth?: number;
23
+ viewportHeight?: number;
24
+ orientation?: "portrait" | "landscape";
25
+ cpuCores?: number;
26
+ connectionType?: string | null;
27
+ };
28
+ /**
29
+ * Complete browser info (essential + extended)
30
+ */
31
+ export type BrowserInfo = BrowserInfoEssential & BrowserInfoExtended;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Essential browser info
3
+ *
4
+ * @typedef {{
5
+ * deviceType?: 'phone'|'tablet'|'tv'|'desktop',
6
+ * isMobile?: boolean,
7
+ * platform?: string,
8
+ * browser?: string,
9
+ * browserEngine?: 'blink'|'gecko'|'webkit'|'unknown',
10
+ * screenWidth?: number,
11
+ * screenHeight?: number,
12
+ * devicePixelRatio?: number,
13
+ * hasTouch?: boolean,
14
+ * language?: string,
15
+ * timezone?: string,
16
+ * isPwa?: boolean
17
+ * }} BrowserInfoEssential
18
+ */
19
+
20
+ /**
21
+ * Extended browser info (opt-in)
22
+ *
23
+ * @typedef {{
24
+ * viewportWidth?: number,
25
+ * viewportHeight?: number,
26
+ * orientation?: 'portrait'|'landscape',
27
+ * cpuCores?: number,
28
+ * connectionType?: string|null
29
+ * }} BrowserInfoExtended
30
+ */
31
+
32
+ /**
33
+ * Complete browser info (essential + extended)
34
+ *
35
+ * @typedef {BrowserInfoEssential & BrowserInfoExtended} BrowserInfo
36
+ */
37
+
38
+ export {};
@@ -3,34 +3,28 @@
3
3
 
4
4
  import {
5
5
  getGameWidthOnLandscape,
6
- getGameWidthOnPortrait,
7
- isIOS,
8
- isIpadOS,
9
- getOS,
10
- getIsMobile
6
+ getGameWidthOnPortrait
11
7
  } from './gamebox.util.js';
12
8
 
9
+ import {
10
+ getIsAppleMobile,
11
+ getIsIpadOS,
12
+ getIsMobile
13
+ } from '../../../browser/info/device.js';
14
+
15
+ import {
16
+ getIsPwa,
17
+ getIsFullscreen
18
+ } from '../../../browser/info/display.js';
19
+
13
20
  import ScaledContainer from './ScaledContainer.svelte';
14
21
 
15
22
  /**
16
- * @typedef {{
17
- * isLandscape: boolean,
18
- * isPortrait: boolean,
19
- * isMobile:boolean,
20
- * isIos:boolean,
21
- * isAndroid:boolean,
22
- * os:'Android'|'iOS',
23
- * isFullscreen:boolean,
24
- * isDevMode:boolean,
25
- * requestDevmode:function,
26
- * requestFullscreen:function,
27
- * gameWidth: number,
28
- * gameHeight: number
29
- * }} SnippetParams
23
+ * @typedef {import('./typedef.js').SnippetParams} SnippetParams
30
24
  */
31
25
 
32
26
  /**
33
- * @typedef {import('svelte').Snippet<[SnippetParams]>} GameBoxSnippet
27
+ * @typedef {import('./typedef.js').GameBoxSnippet} GameBoxSnippet
34
28
  */
35
29
 
36
30
  /**
@@ -97,11 +91,6 @@
97
91
  let windowWidth = $state();
98
92
  let windowHeight = $state();
99
93
 
100
- let debouncedWindowWidth = $state();
101
- let debouncedWindowHeight = $state();
102
-
103
- let debounceTimer;
104
-
105
94
  let gameWidthOnPortrait = $state();
106
95
  let gameHeightOnPortrait = $state();
107
96
 
@@ -132,47 +121,19 @@
132
121
  } );
133
122
 
134
123
  // iPad is also considered Apple mobile
135
- const isAppleMobile = isIOS();
136
-
137
- let os = $state();
138
- let isIos = $derived(os === 'iOS');
139
- let isAndroid = $derived(os === 'Android');
140
-
141
- // Debounce window dimensions on iOS to skip intermediate resize events
142
- let skipNextResize = false;
143
- let resetTimer;
144
-
145
- $effect(() => {
146
- if (isAppleMobile && windowWidth && windowHeight) {
147
- if (skipNextResize) {
148
- skipNextResize = false;
149
- return; // Skip first of the two resize events
150
- }
124
+ const isAppleMobile = getIsAppleMobile();
151
125
 
152
- // skipNextResize = true; // disabled to test <<
126
+ let isIos = $state(false);
127
+ let isAndroid = $state(false);
128
+ let isIpadOS = $state(false);
153
129
 
154
- debouncedWindowWidth = windowWidth;
155
- debouncedWindowHeight = windowHeight;
156
-
157
- // Reset flag after resize events settle
158
- clearTimeout(resetTimer);
159
- resetTimer = setTimeout(() => {
160
- skipNextResize = false;
161
- }, 500);
162
- } else {
163
- // Non-iOS: use dimensions immediately
164
- debouncedWindowWidth = windowWidth;
165
- debouncedWindowHeight = windowHeight;
166
- }
167
- });
168
-
169
- // Update iOS dimensions when debounced window size changes
130
+ // Update iOS dimensions when window size changes
170
131
  $effect(() => {
171
132
  if (
172
133
  isPwa &&
173
134
  isAppleMobile &&
174
- debouncedWindowWidth &&
175
- debouncedWindowHeight
135
+ windowWidth &&
136
+ windowHeight
176
137
  ) {
177
138
  updateIosWidthHeight();
178
139
  }
@@ -285,13 +246,13 @@
285
246
 
286
247
  isMobile = getIsMobile();
287
248
 
288
- os = getOS();
249
+ isIos = isAppleMobile;
250
+ isAndroid = !isAppleMobile && /Android/.test(navigator.userAgent);
251
+ isIpadOS = getIsIpadOS();
289
252
 
290
253
  isFullscreen = getIsFullscreen();
291
254
 
292
- isPwa = window.matchMedia(
293
- '(display-mode: fullscreen) or (display-mode: standalone)'
294
- ).matches;
255
+ isPwa = getIsPwa();
295
256
 
296
257
  updateIosWidthHeight();
297
258
 
@@ -364,26 +325,6 @@
364
325
  }
365
326
  });
366
327
 
367
- /**
368
- * Returns true if the window is in full screen
369
- * - Checks if CSS thinks we're in fullscreen mode
370
- * - Checks if there is a fullscreen element (for safari)
371
- */
372
- function getIsFullscreen() {
373
- if (
374
- window.matchMedia(
375
- '(display-mode: fullscreen) or (display-mode: standalone)'
376
- ).matches
377
- ) {
378
- return true;
379
- } else if (document.fullscreenElement) {
380
- // Safari
381
- return true;
382
- }
383
-
384
- return false;
385
- }
386
-
387
328
  async function requestFullscreen() {
388
329
  // console.debug('Request full screen');
389
330
  show = false;
@@ -484,7 +425,8 @@
484
425
  isMobile,
485
426
  isIos,
486
427
  isAndroid,
487
- os,
428
+ isIpadOS,
429
+ isPwa,
488
430
  isFullscreen,
489
431
  isDevMode,
490
432
  requestDevmode,
@@ -508,7 +450,8 @@
508
450
  isMobile,
509
451
  isIos,
510
452
  isAndroid,
511
- os,
453
+ isIpadOS,
454
+ isPwa,
512
455
  isFullscreen,
513
456
  isDevMode,
514
457
  requestDevmode,
@@ -532,7 +475,8 @@
532
475
  isMobile,
533
476
  isIos,
534
477
  isAndroid,
535
- os,
478
+ isIpadOS,
479
+ isPwa,
536
480
  isFullscreen,
537
481
  isDevMode,
538
482
  requestDevmode,
@@ -556,7 +500,8 @@
556
500
  isMobile,
557
501
  isIos,
558
502
  isAndroid,
559
- os,
503
+ isIpadOS,
504
+ isPwa,
560
505
  isFullscreen,
561
506
  isDevMode,
562
507
  requestDevmode,
@@ -581,7 +526,8 @@
581
526
  isMobile,
582
527
  isIos,
583
528
  isAndroid,
584
- os,
529
+ isIpadOS,
530
+ isPwa,
585
531
  isFullscreen,
586
532
  isDevMode,
587
533
  requestDevmode,
@@ -605,7 +551,8 @@
605
551
  isMobile,
606
552
  isIos,
607
553
  isAndroid,
608
- os,
554
+ isIpadOS,
555
+ isPwa,
609
556
  isFullscreen,
610
557
  isDevMode,
611
558
  requestDevmode,